//===--- PILGenLValue.cpp - Constructs logical lvalues for PILGen ---------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Emission of l-value expressions and basic operations on them.
//
//===----------------------------------------------------------------------===//

#include "polarphp/pil/gen/AstVisitor.h"
#include "polarphp/pil/gen/ArgumentScope.h"
#include "polarphp/pil/gen/ArgumentSource.h"
#include "polarphp/pil/gen/Conversion.h"
#include "polarphp/pil/gen/Initialization.h"
#include "polarphp/pil/gen/LValue.h"
#include "polarphp/pil/gen/RValue.h"
#include "polarphp/pil/gen/PILGen.h"
#include "polarphp/pil/gen/Scope.h"
#include "polarphp/ast/DiagnosticsCommon.h"
#include "polarphp/ast/DiagnosticsPIL.h"
#include "polarphp/ast/GenericEnvironment.h"
#include "polarphp/ast/PropertyWrappers.h"
#include "polarphp/ast/TypeCheckRequests.h"
#include "polarphp/pil/lang/InstructionUtils.h"
#include "polarphp/pil/lang/MemAccessUtils.h"
#include "polarphp/pil/lang/PrettyStackTrace.h"
#include "polarphp/pil/lang/PILArgument.h"
#include "polarphp/pil/lang/PILUndef.h"
#include "polarphp/pil/lang/TypeLowering.h"
#include "llvm/Support/raw_ostream.h"

using namespace polar;
using namespace lowering;

//===----------------------------------------------------------------------===//

namespace {

struct LValueWritebackCleanup : Cleanup {
   FormalEvaluationContext::stable_iterator Depth;

   LValueWritebackCleanup() : Depth() {}

   void emit(PILGenFunction &SGF, CleanupLocation loc,
             ForUnwind_t forUnwind) override {
      FullExpr scope(SGF.Cleanups, loc);

      // TODO: honor forUnwind!
      auto &evaluation = getEvaluation(SGF);
      evaluation.performWriteback(SGF, /*isFinal*/ false);
   }

   void dump(PILGenFunction &) const override {
#ifndef NDEBUG
      llvm::errs() << "LValueWritebackCleanup\n"
                   << "State: " << getState() << " Depth: " << Depth.getDepth()
                   << "\n";
#endif
   }

private:
   ExclusiveBorrowFormalAccess &getEvaluation(PILGenFunction &SGF) {
      auto &evaluation = *SGF.FormalEvalContext.find(Depth);
      assert(evaluation.getKind() == FormalAccess::Exclusive);
      return static_cast<ExclusiveBorrowFormalAccess &>(evaluation);
   }
};

} // end anonymous namespace

/// Push a writeback onto the current LValueWriteback stack.
static void pushWriteback(PILGenFunction &SGF,
                          PILLocation loc,
                          std::unique_ptr<LogicalPathComponent> &&comp,
                          ManagedValue base,
                          MaterializedLValue materialized) {
   assert(SGF.isInFormalEvaluationScope());

   // Push a cleanup to execute the writeback consistently.
   auto &context = SGF.FormalEvalContext;
   LValueWritebackCleanup &cleanup =
      SGF.Cleanups.pushCleanup<LValueWritebackCleanup>();
   CleanupHandle handle = SGF.Cleanups.getTopCleanup();

   context.push<ExclusiveBorrowFormalAccess>(loc, std::move(comp), base,
                                             materialized, handle);
   cleanup.Depth = context.stable_begin();
}

static bool areCertainlyEqualIndices(const Expr *e1, const Expr *e2);

void ExclusiveBorrowFormalAccess::diagnoseConflict(
   const ExclusiveBorrowFormalAccess &rhs,
   PILGenFunction &SGF) const {
   // If the two writebacks we're comparing are of different kinds (e.g.
   // ownership conversion vs a computed property) then they aren't the
   // same and thus cannot conflict.
   if (component->getKind() != rhs.component->getKind())
      return;

   // If the lvalues don't have the same base value (possibly null), then
   // they aren't the same.  Note that this is the primary source of false
   // negative for this diagnostic.
   PILValue lhsBaseValue = base.getValue(), rhsBaseValue = rhs.base.getValue();
   if (lhsBaseValue != rhsBaseValue &&
       (!lhsBaseValue || !rhsBaseValue ||
        !RValue::areObviouslySameValue(lhsBaseValue, rhsBaseValue))) {
      return;
   }

   auto lhsStorage = component->getAccessedStorage();
   if (!lhsStorage) return;

   auto rhsStorage = rhs.component->getAccessedStorage();
   if (!rhsStorage) return;

   // If the decls match, then this could conflict.
   if (lhsStorage->Storage != rhsStorage->Storage ||
       !lhsStorage->Storage ||
       lhsStorage->IsSuper != rhsStorage->IsSuper)
      return;

   assert((lhsStorage->Indices != nullptr) == (rhsStorage->Indices != nullptr));

   auto storage = lhsStorage->Storage;

   // If the decl is monomorphically a stored property, allow aliases.
   // It could be overridden by a computed property in a subclass, but
   // that's not likely enough to be worth the strictness here.
   auto impl = storage->getImplInfo();
   // TODO: Stored properties with didSet accessors that don't look at the
   // oldValue could also be addressed.
   if ((impl.getReadImpl() == ReadImplKind::Stored ||
        impl.getReadImpl() == ReadImplKind::Address) &&
       (impl.getWriteImpl() == WriteImplKind::Immutable ||
        impl.getWriteImpl() == WriteImplKind::Stored ||
        impl.getWriteImpl() == WriteImplKind::MutableAddress)) {
      return;
   }

   // If the property is a generic requirement, allow aliases, because
   // it may be conformed to using a stored property.
   if (isa<InterfaceDecl>(storage->getDeclContext()))
      return;

   // If this is a simple property access, then we must have a conflict.
   if (!lhsStorage->Indices) {
      assert(isa<VarDecl>(storage));
      SGF.SGM.diagnose(loc, diag::writeback_overlap_property,
                       storage->getBaseName().getIdentifier())
         .highlight(loc.getSourceRange());
      SGF.SGM.diagnose(rhs.loc, diag::writebackoverlap_note)
         .highlight(rhs.loc.getSourceRange());
      return;
   }

   // Otherwise, it is a subscript, check the index values.

   // If the indices are literally identical PILValue's, then there is
   // clearly a conflict.
   if (!lhsStorage->Indices->isObviouslyEqual(*rhsStorage->Indices)) {
      // If the index value doesn't lower to literally the same PILValue's,
      // do some fuzzy matching to catch the common case.
      if (!lhsStorage->IndexExprForDiagnostics ||
          !rhsStorage->IndexExprForDiagnostics ||
          !areCertainlyEqualIndices(lhsStorage->IndexExprForDiagnostics,
                                    rhsStorage->IndexExprForDiagnostics))
         return;
   }

   // The locations for the subscripts are almost certainly SubscriptExprs.
   // If so, dig into them to produce better location info in the
   // diagnostics and be able to do more precise analysis.
   auto expr1 = loc.getAsAstNode<SubscriptExpr>();
   auto expr2 = rhs.loc.getAsAstNode<SubscriptExpr>();

   if (expr1 && expr2) {
      SGF.SGM.diagnose(loc, diag::writeback_overlap_subscript)
         .highlight(expr1->getBase()->getSourceRange());

      SGF.SGM.diagnose(rhs.loc, diag::writebackoverlap_note)
         .highlight(expr2->getBase()->getSourceRange());

   } else {
      SGF.SGM.diagnose(loc, diag::writeback_overlap_subscript)
         .highlight(loc.getSourceRange());
      SGF.SGM.diagnose(rhs.loc, diag::writebackoverlap_note)
         .highlight(rhs.loc.getSourceRange());
   }
}

//===----------------------------------------------------------------------===//

static CanType getSubstFormalRValueType(Expr *expr) {
   return expr->getType()->getRValueType()->getCanonicalType();
}

static LValueTypeData getAbstractedTypeData(TypeExpansionContext context,
                                            PILGenModule &SGM,
                                            SGFAccessKind accessKind,
                                            AbstractionPattern origFormalType,
                                            CanType substFormalType) {
   return {
      accessKind, origFormalType, substFormalType,
      SGM.Types.getLoweredRValueType(context, origFormalType, substFormalType)};
}

static LValueTypeData getLogicalStorageTypeData(TypeExpansionContext context,
                                                PILGenModule &SGM,
                                                SGFAccessKind accessKind,
                                                CanType substFormalType) {
   assert(!isa<ReferenceStorageType>(substFormalType));
   AbstractionPattern origFormalType(
      substFormalType.getReferenceStorageReferent());
   return getAbstractedTypeData(context, SGM, accessKind, origFormalType,
                                substFormalType);
}

static LValueTypeData getPhysicalStorageTypeData(TypeExpansionContext context,
                                                 PILGenModule &SGM,
                                                 SGFAccessKind accessKind,
                                                 AbstractStorageDecl *storage,
                                                 CanType substFormalType) {
   assert(!isa<ReferenceStorageType>(substFormalType));
   auto origFormalType = SGM.Types.getAbstractionPattern(storage)
      .getReferenceStorageReferentType();
   return getAbstractedTypeData(context, SGM, accessKind, origFormalType,
                                substFormalType);
}

static bool shouldUseUnsafeEnforcement(VarDecl *var) {
   if (var->isDebuggerVar())
      return true;

   // TODO: Check for the explicit "unsafe" attribute.
   return false;
}

Optional<PILAccessEnforcement>
PILGenFunction::getStaticEnforcement(VarDecl *var) {
   if (var && shouldUseUnsafeEnforcement(var))
      return PILAccessEnforcement::Unsafe;

   return PILAccessEnforcement::Static;
}

Optional<PILAccessEnforcement>
PILGenFunction::getDynamicEnforcement(VarDecl *var) {
   if (getOptions().EnforceExclusivityDynamic) {
      if (var && shouldUseUnsafeEnforcement(var))
         return PILAccessEnforcement::Unsafe;
      return PILAccessEnforcement::Dynamic;
   }
   return None;
}

Optional<PILAccessEnforcement>
PILGenFunction::getUnknownEnforcement(VarDecl *var) {
   if (var && shouldUseUnsafeEnforcement(var))
      return PILAccessEnforcement::Unsafe;

   return PILAccessEnforcement::Unknown;
}

/// PILGenLValue - An AstVisitor for building logical lvalues.
class LLVM_LIBRARY_VISIBILITY PILGenLValue
   : public lowering::ExprVisitor<PILGenLValue, LValue,
      SGFAccessKind, LValueOptions>
{

public:
   PILGenFunction &SGF;
   PILGenLValue(PILGenFunction &SGF) : SGF(SGF) {}

   LValue visitRec(Expr *e, SGFAccessKind accessKind, LValueOptions options,
                   AbstractionPattern orig = AbstractionPattern::getInvalid());

   /// Dummy handler to log unimplemented nodes.
   LValue visitExpr(Expr *e, SGFAccessKind accessKind, LValueOptions options);

   // Nodes that form the root of lvalue paths
   LValue visitDiscardAssignmentExpr(DiscardAssignmentExpr *e,
                                     SGFAccessKind accessKind,
                                     LValueOptions options);
   LValue visitDeclRefExpr(DeclRefExpr *e, SGFAccessKind accessKind,
                           LValueOptions options);
   LValue visitOpaqueValueExpr(OpaqueValueExpr *e, SGFAccessKind accessKind,
                               LValueOptions options);

   // Nodes that make up components of lvalue paths

   LValue visitMemberRefExpr(MemberRefExpr *e, SGFAccessKind accessKind,
                             LValueOptions options);
   LValue visitSubscriptExpr(SubscriptExpr *e, SGFAccessKind accessKind,
                             LValueOptions options);
   LValue visitTupleElementExpr(TupleElementExpr *e, SGFAccessKind accessKind,
                                LValueOptions options);
   LValue visitForceValueExpr(ForceValueExpr *e, SGFAccessKind accessKind,
                              LValueOptions options);
   LValue visitBindOptionalExpr(BindOptionalExpr *e, SGFAccessKind accessKind,
                                LValueOptions options);
   LValue visitOpenExistentialExpr(OpenExistentialExpr *e,
                                   SGFAccessKind accessKind,
                                   LValueOptions options);
   LValue visitKeyPathApplicationExpr(KeyPathApplicationExpr *e,
                                      SGFAccessKind accessKind,
                                      LValueOptions options);

   // Expressions that wrap lvalues

   LValue visitInOutExpr(InOutExpr *e, SGFAccessKind accessKind,
                         LValueOptions options);
   LValue visitDotSyntaxBaseIgnoredExpr(DotSyntaxBaseIgnoredExpr *e,
                                        SGFAccessKind accessKind,
                                        LValueOptions options);
};

/// Read this component.
ManagedValue
LogicalPathComponent::projectForRead(PILGenFunction &SGF, PILLocation loc,
                                     ManagedValue base,
                                     SGFAccessKind accessKind) && {
   assert(isReadAccess(accessKind));

   const TypeLowering &RValueTL = SGF.getTypeLowering(getTypeOfRValue());

   // If the access doesn't require us to make an owned address, don't
   // force a materialization.
   if (!isReadAccessResultAddress(accessKind)) {
      auto rvalue = std::move(*this).get(SGF, loc, base, SGFContext());
      return std::move(rvalue).getAsSingleValue(SGF, loc);
   }

   TemporaryInitializationPtr tempInit;
   RValue rvalue;

   // If the RValue type has an openedExistential, then the RValue must be
   // materialized before allocating a temporary for the RValue type. In that
   // case, the RValue cannot be emitted directly into the temporary.
   if (getTypeOfRValue().hasOpenedExistential()) {
      // Emit a 'get'.
      rvalue = std::move(*this).get(SGF, loc, base, SGFContext());

      // Create a temporary, whose type may depend on the 'get'.
      tempInit = SGF.emitFormalAccessTemporary(loc, RValueTL);
   } else {
      // Create a temporary for a static (non-dependent) RValue type.
      tempInit = SGF.emitFormalAccessTemporary(loc, RValueTL);

      // Emit a 'get' directly into the temporary.
      rvalue = std::move(*this).get(SGF, loc, base, SGFContext(tempInit.get()));
   }
   // `this` is now dead.

   // Force `value` into a temporary if is wasn't emitted there.
   if (!rvalue.isInContext())
      std::move(rvalue).forwardInto(SGF, loc, tempInit.get());

   return tempInit->getManagedAddress();
}

ManagedValue LogicalPathComponent::project(PILGenFunction &SGF,
                                           PILLocation loc,
                                           ManagedValue base) && {
   auto accessKind = getAccessKind();
   if (isReadAccess(accessKind))
      return std::move(*this).projectForRead(SGF, loc, base, accessKind);

   // AccessKind is Write or ReadWrite. We need to emit a get and set.
   assert(SGF.isInFormalEvaluationScope() &&
          "materializing l-value for modification without writeback scope");

   // Clone anything else about the component that we might need in the
   // writeback.
   auto clonedComponent = clone(SGF, loc);

   ManagedValue temp =
      std::move(*this).projectForRead(SGF, loc, base,
                                      SGFAccessKind::OwnedAddressRead);

   if (SGF.getOptions().VerifyExclusivity) {
      // Begin an access of the temporary. It is unenforced because enforcement
      // isn't required for RValues.
      PILValue accessAddress = UnenforcedFormalAccess::enter(
         SGF, loc, temp.getValue(), PILAccessKind::Modify);
      temp = std::move(temp).transform(accessAddress);
   }
   // Push a writeback for the temporary.
   pushWriteback(SGF, loc, std::move(clonedComponent), base,
                 MaterializedLValue(temp));
   return ManagedValue::forLValue(temp.getValue());
}

void LogicalPathComponent::writeback(PILGenFunction &SGF, PILLocation loc,
                                     ManagedValue base,
                                     MaterializedLValue materialized,
                                     bool isFinal) {
   assert(!materialized.callback &&
          "unexpected materialized lvalue with callback!");

   // Load the value from the temporary unless the type is address-only
   // and this is the final use, in which case we can just consume the
   // value as-is.
   auto temporary = materialized.temporary;

   assert(temporary.getType().isAddress());
   auto &tempTL = SGF.getTypeLowering(temporary.getType());
   if (!tempTL.isAddressOnly() || !isFinal ||
       !SGF.silConv.useLoweredAddresses()) {
      if (isFinal) temporary.forward(SGF);
      temporary = SGF.emitLoad(loc, temporary.getValue(), tempTL,
                               SGFContext(), IsTake_t(isFinal));
   }
   RValue rvalue(SGF, loc, getSubstFormalType(), temporary);

   // Don't consume cleanups on the base if this isn't final.
   if (base && !isFinal) { base = ManagedValue::forUnmanaged(base.getValue()); }

   // Clone the component if this isn't final.
   std::unique_ptr<LogicalPathComponent> clonedComponent =
      (isFinal ? nullptr : clone(SGF, loc));
   LogicalPathComponent *component = (isFinal ? this : &*clonedComponent);
   std::move(*component).set(SGF, loc, ArgumentSource(loc, std::move(rvalue)),
                             base);
}

InOutConversionScope::InOutConversionScope(PILGenFunction &SGF)
   : SGF(SGF)
{
   assert(SGF.isInFormalEvaluationScope()
          && "inout conversions should happen in writeback scopes");
   assert(!SGF.InInOutConversionScope
          && "inout conversions should not be nested");
   SGF.InInOutConversionScope = true;
}

InOutConversionScope::~InOutConversionScope() {
   assert(SGF.InInOutConversionScope && "already exited conversion scope?!");
   SGF.InInOutConversionScope = false;
}

void PathComponent::_anchor() {}
void PhysicalPathComponent::_anchor() {}

void PathComponent::dump() const {
   dump(llvm::errs());
}

/// Return the LValueTypeData for a PIL value with the given Ast formal type.
static LValueTypeData getValueTypeData(SGFAccessKind accessKind,
                                       CanType formalType, PILValue value) {
   return {
      accessKind,
      AbstractionPattern(formalType),
      formalType,
      value->getType().getAstType(),
   };
}
static LValueTypeData getValueTypeData(PILGenFunction &SGF,
                                       SGFAccessKind accessKind, Expr *e) {
   CanType formalType = getSubstFormalRValueType(e);
   CanType loweredType = SGF.getLoweredType(formalType).getAstType();

   return {
      accessKind,
      AbstractionPattern(formalType),
      formalType,
      loweredType
   };
}

/// Given the address of an optional value, unsafely project out the
/// address of the value.
static ManagedValue getAddressOfOptionalValue(PILGenFunction &SGF,
                                              PILLocation loc,
                                              ManagedValue optAddr,
                                              const LValueTypeData &valueTypeData) {
   // Project out the 'Some' payload.
   EnumElementDecl *someDecl = SGF.getAstContext().getOptionalSomeDecl();

   // If the base is +1, we want to forward the cleanup.
   bool hadCleanup = optAddr.hasCleanup();

   // UncheckedTakeEnumDataAddr is safe to apply to Optional, because it is
   // a single-payload enum. There will (currently) never be spare bits
   // embedded in the payload.
   PILValue valueAddr =
      SGF.B.createUncheckedTakeEnumDataAddr(loc, optAddr.forward(SGF), someDecl,
                                            PILType::getPrimitiveAddressType(
                                               valueTypeData.TypeOfRValue));

   // Return the value as +1 if the optional was +1.
   if (hadCleanup) {
      return SGF.emitManagedBufferWithCleanup(valueAddr);
   } else {
      return ManagedValue::forLValue(valueAddr);
   }
}

namespace {
/// A helper class for creating writebacks associated with l-value
/// components that don't really need them.
class WritebackPseudoComponent : public LogicalPathComponent {
protected:
   WritebackPseudoComponent(const LValueTypeData &typeData)
      : LogicalPathComponent(typeData, WritebackPseudoKind) {}

public:
   std::unique_ptr<LogicalPathComponent>
   clone(PILGenFunction &SGF, PILLocation l) const override {
      llvm_unreachable("called clone on pseudo-component");
   }

   RValue get(PILGenFunction &SGF, PILLocation loc,
              ManagedValue base, SGFContext c) && override {
      llvm_unreachable("called get on a pseudo-component");
   }
   void set(PILGenFunction &SGF, PILLocation loc,
            ArgumentSource &&value, ManagedValue base) && override {
      llvm_unreachable("called set on a pseudo-component");
   }
   ManagedValue project(PILGenFunction &SGF, PILLocation loc,
                        ManagedValue base) && override {
      llvm_unreachable("called project on a pseudo-component");
   }

   Optional<AccessedStorage> getAccessedStorage() const override {
      return None;
   }
};

class EndAccessPseudoComponent : public WritebackPseudoComponent {
public:
   EndAccessPseudoComponent(const LValueTypeData &typeData)
      : WritebackPseudoComponent(typeData) {}

private:
   void writeback(PILGenFunction &SGF, PILLocation loc,
                  ManagedValue base,
                  MaterializedLValue materialized,
                  bool isFinal) override {
      loc.markAutoGenerated();

      assert(base.isLValue());
      SGF.B.createEndAccess(loc, base.getValue(), /*abort*/ false);
   }

   void dump(raw_ostream &OS, unsigned indent) const override {
      OS.indent(indent) << "EndAccessPseudoComponent\n";
   }
};
} // end anonymous namespace

static PILValue enterAccessScope(PILGenFunction &SGF, PILLocation loc,
                                 PILValue addr, LValueTypeData typeData,
                                 SGFAccessKind accessKind,
                                 PILAccessEnforcement enforcement) {
   auto silAccessKind = isReadAccess(accessKind) ? PILAccessKind::Read
                                                 : PILAccessKind::Modify;

   assert(SGF.isInFormalEvaluationScope() &&
          "tried to enter access scope without a writeback scope!");

   // Enter the access.
   addr = SGF.B.createBeginAccess(loc, addr, silAccessKind, enforcement,
      /*hasNoNestedConflict=*/false,
      /*fromBuiltin=*/false);

   // Push a writeback to end it.
   auto accessedMV = ManagedValue::forLValue(addr);
   std::unique_ptr<LogicalPathComponent>
      component(new EndAccessPseudoComponent(typeData));
   pushWriteback(SGF, loc, std::move(component), accessedMV,
                 MaterializedLValue());

   return addr;
}

static ManagedValue enterAccessScope(PILGenFunction &SGF, PILLocation loc,
                                     ManagedValue addr, LValueTypeData typeData,
                                     SGFAccessKind accessKind,
                                     PILAccessEnforcement enforcement) {
   return ManagedValue::forLValue(
      enterAccessScope(SGF, loc, addr.getLValueAddress(), typeData,
                       accessKind, enforcement));
}

// Find the base of the formal access at `address`. If the base requires an
// access marker, then create a begin_access on `address`. Return the
// address to be used for the access.
//
// FIXME: In order to generate more consistent and verifiable PIL patterns, or
// subobject projections, create the access on the base address and recreate the
// projection.
PILValue UnenforcedAccess::beginAccess(PILGenFunction &SGF, PILLocation loc,
                                       PILValue address, PILAccessKind kind) {
   if (!SGF.getOptions().VerifyExclusivity)
      return address;

   const AccessedStorage &storage = findAccessedStorage(address);
   // Unsafe access may have invalid storage (e.g. a RawPointer).
   if (storage && !isPossibleFormalAccessBase(storage, &SGF.F))
      return address;

   auto BAI =
      SGF.B.createBeginAccess(loc, address, kind, PILAccessEnforcement::Unsafe,
         /*hasNoNestedConflict=*/false,
         /*fromBuiltin=*/false);
   beginAccessPtr = BeginAccessPtr(BAI, DeleterCheck());

   return BAI;
}

void UnenforcedAccess::endAccess(PILGenFunction &SGF) {
   emitEndAccess(SGF);
   beginAccessPtr.release();
}

void UnenforcedAccess::emitEndAccess(PILGenFunction &SGF) {
   if (!beginAccessPtr)
      return;

   SGF.B.createEndAccess(beginAccessPtr->getLoc(), beginAccessPtr.get(),
      /*abort*/ false);
}

// Emit an end_access marker when executing a cleanup (on a side branch).
void UnenforcedFormalAccess::emitEndAccess(PILGenFunction &SGF) {
   access.emitEndAccess(SGF);
}

// End the access when existing the FormalEvaluationScope.
void UnenforcedFormalAccess::finishImpl(PILGenFunction &SGF) {
   access.endAccess(SGF);
}

namespace {
struct UnenforcedAccessCleanup : Cleanup {
   FormalEvaluationContext::stable_iterator Depth;

   UnenforcedAccessCleanup() : Depth() {}

   void emit(PILGenFunction &SGF, CleanupLocation loc,
             ForUnwind_t forUnwind) override {
      auto &evaluation = *SGF.FormalEvalContext.find(Depth);
      assert(evaluation.getKind() == FormalAccess::Unenforced);
      auto &formalAccess = static_cast<UnenforcedFormalAccess &>(evaluation);
      formalAccess.emitEndAccess(SGF);
   }

   void dump(PILGenFunction &) const override {
#ifndef NDEBUG
      llvm::errs() << "UnenforcedAccessCleanup\n"
                   << "State: " << getState() << " Depth: " << Depth.getDepth()
                   << "\n";
#endif
   }
};
} // end anonymous namespace

PILValue UnenforcedFormalAccess::enter(PILGenFunction &SGF, PILLocation loc,
                                       PILValue address, PILAccessKind kind) {
   assert(SGF.isInFormalEvaluationScope());

   UnenforcedAccess access;
   PILValue accessAddress = access.beginAccess(SGF, loc, address, kind);
   if (!access.beginAccessPtr)
      return address;

   auto &cleanup = SGF.Cleanups.pushCleanup<UnenforcedAccessCleanup>();
   CleanupHandle handle = SGF.Cleanups.getTopCleanup();
   auto &context = SGF.FormalEvalContext;
   context.push<UnenforcedFormalAccess>(loc, std::move(access), handle);
   cleanup.Depth = context.stable_begin();

   return accessAddress;
}

static void copyBorrowedYieldsIntoTemporary(PILGenFunction &SGF,
                                            PILLocation loc,
                                            ArrayRef<ManagedValue> &yields,
                                            AbstractionPattern origFormalType,
                                            CanType substFormalType,
                                            Initialization *init) {
   if (!origFormalType.isTuple()) {
      auto value = yields.front();
      yields = yields.drop_front();
      init->copyOrInitValueInto(SGF, loc, value, /*isInit*/ false);
      init->finishInitialization(SGF);
      return;
   }

   assert(init->canSplitIntoTupleElements());
   SmallVector<InitializationPtr, 4> scratch;
   auto eltInits =
      init->splitIntoTupleElements(SGF, loc, substFormalType, scratch);
   for (size_t i : indices(eltInits)) {
      auto origEltType = origFormalType.getTupleElementType(i);
      auto substEltType = cast<TupleType>(substFormalType).getElementType(i);
      copyBorrowedYieldsIntoTemporary(SGF, loc, yields, origEltType,
                                      substEltType, eltInits[i].get());
   }
   init->finishInitialization(SGF);
}

namespace {
class RefElementComponent : public PhysicalPathComponent {
   VarDecl *Field;
   PILType SubstFieldType;
   bool IsNonAccessing;
public:
   RefElementComponent(VarDecl *field, LValueOptions options,
                       PILType substFieldType, LValueTypeData typeData)
      : PhysicalPathComponent(typeData, RefElementKind),
        Field(field), SubstFieldType(substFieldType),
        IsNonAccessing(options.IsNonAccessing) {}

   virtual bool isLoadingPure() const override { return true; }

   ManagedValue project(PILGenFunction &SGF, PILLocation loc,
                        ManagedValue base) && override {
      assert(base.getType().hasReferenceSemantics() &&
             "base for ref element component must be a reference type");

      // Borrow the ref element addr using formal access. If we need the ref
      // element addr, we will load it in this expression.
      if (base.getType().isAddress()) {
         base = SGF.B.createFormalAccessLoadBorrow(loc, base);
      } else {
         base = base.formalAccessBorrow(SGF, loc);
      }
      PILValue result =
         SGF.B.createRefElementAddr(loc, base.getUnmanagedValue(),
                                    Field, SubstFieldType);

      // Avoid emitting access markers completely for non-accesses or immutable
      // declarations. Access marker verification is aware of these cases.
      if (!IsNonAccessing && !Field->isLet()) {
         if (auto enforcement = SGF.getDynamicEnforcement(Field)) {
            result = enterAccessScope(SGF, loc, result, getTypeData(),
                                      getAccessKind(), *enforcement);
         }
      }

      return ManagedValue::forLValue(result);
   }

   void dump(raw_ostream &OS, unsigned indent) const override {
      OS.indent(indent) << "RefElementComponent(" << Field->getName() << ")\n";
   }
};

class TupleElementComponent : public PhysicalPathComponent {
   unsigned ElementIndex;
public:
   TupleElementComponent(unsigned elementIndex, LValueTypeData typeData)
      : PhysicalPathComponent(typeData, TupleElementKind),
        ElementIndex(elementIndex) {}

   virtual bool isLoadingPure() const override { return true; }

   ManagedValue project(PILGenFunction &SGF, PILLocation loc,
                        ManagedValue base) && override {
      assert(base && "invalid value for element base");
      if (base.getType().isObject()) {
         return SGF.B.createTupleExtract(loc, base, ElementIndex);
      }

      // TODO: if the base is +1, break apart its cleanup.
      auto Res = SGF.B.createTupleElementAddr(loc, base.getValue(),
                                              ElementIndex,
                                              getTypeOfRValue().getAddressType());
      return ManagedValue::forLValue(Res);
   }

   void dump(raw_ostream &OS, unsigned indent) const override {
      OS.indent(indent) << "TupleElementComponent(" << ElementIndex << ")\n";
   }
};

class StructElementComponent : public PhysicalPathComponent {
   VarDecl *Field;
   PILType SubstFieldType;
public:
   StructElementComponent(VarDecl *field, PILType substFieldType,
                          LValueTypeData typeData)
      : PhysicalPathComponent(typeData, StructElementKind),
        Field(field), SubstFieldType(substFieldType) {}

   virtual bool isLoadingPure() const override { return true; }

   ManagedValue project(PILGenFunction &SGF, PILLocation loc,
                        ManagedValue base) && override {
      assert(base && "invalid value for element base");
      if (base.getType().isObject()) {
         return SGF.B.createStructExtract(loc, base, Field);
      }

      // TODO: if the base is +1, break apart its cleanup.
      auto Res = SGF.B.createStructElementAddr(loc, base.getValue(),
                                               Field, SubstFieldType);
      return ManagedValue::forLValue(Res);
   }
   void dump(raw_ostream &OS, unsigned indent) const override {
      OS.indent(indent) << "StructElementComponent("
                        << Field->getName() << ")\n";
   }
};

/// A physical path component which force-projects the address of
/// the value of an optional l-value.
class ForceOptionalObjectComponent : public PhysicalPathComponent {
   bool isImplicitUnwrap;
public:
   ForceOptionalObjectComponent(LValueTypeData typeData,
                                bool isImplicitUnwrap)
      : PhysicalPathComponent(typeData, OptionalObjectKind),
        isImplicitUnwrap(isImplicitUnwrap) {}

   ManagedValue project(PILGenFunction &SGF, PILLocation loc,
                        ManagedValue base) && override {
      // Assert that the optional value is present and return the projected out
      // payload.
      return SGF.emitPreconditionOptionalHasValue(loc, base, isImplicitUnwrap);
   }

   void dump(raw_ostream &OS, unsigned indent) const override {
      OS.indent(indent) << "ForceOptionalObjectComponent(" << isImplicitUnwrap << ")\n";
   }
};

/// A physical path component which projects out an opened archetype
/// from an existential.
class OpenOpaqueExistentialComponent : public PhysicalPathComponent {
public:
   OpenOpaqueExistentialComponent(CanArchetypeType openedArchetype,
                                  LValueTypeData typeData)
      : PhysicalPathComponent(typeData, OpenOpaqueExistentialKind) {
      assert(getSubstFormalType() == openedArchetype);
   }

   virtual bool isLoadingPure() const override { return true; }

   ManagedValue project(PILGenFunction &SGF, PILLocation loc,
                        ManagedValue base) && override {
      assert(base.getType().isExistentialType() &&
             "base for open existential component must be an existential");
      assert((base.getType().isAddress() ||
              base.getType().getPreferredExistentialRepresentation() ==
              ExistentialRepresentation::Boxed) &&
             "base value of open-existential component was not an address or a "
             "boxed existential?");
      PILValue addr;

      auto rep = base.getType().getPreferredExistentialRepresentation();
      switch (rep) {
         case ExistentialRepresentation::Opaque:
            addr = SGF.B.createOpenExistentialAddr(
               loc, base.getValue(), getTypeOfRValue().getAddressType(),
               getOpenedExistentialAccessFor(getFormalAccessKind(getAccessKind())));
            break;
         case ExistentialRepresentation::Boxed: {
            ManagedValue error;
            if (base.getType().isObject()) {
               error = base;
            } else {
               auto &TL = SGF.getTypeLowering(base.getType());
               error = SGF.emitLoad(loc, base.getValue(), TL,
                                    SGFContext(), IsNotTake);
            }
            addr = SGF.B.createOpenExistentialBox(
               loc, error.getValue(), getTypeOfRValue().getAddressType());
            break;
         }
         default:
            llvm_unreachable("Bad existential representation for address-only type");
      }

      return ManagedValue::forLValue(addr);
   }

   void dump(raw_ostream &OS, unsigned indent) const override {
      OS.indent(indent) << "OpenOpaqueExistentialComponent("
                        << getSubstFormalType() << ")\n";
   }
};

/// A local path component for the payload of a class or metatype existential.
///
/// TODO: Could be physical if we had a way to project out the
/// payload.
class OpenNonOpaqueExistentialComponent : public LogicalPathComponent {
   CanArchetypeType OpenedArchetype;
public:
   OpenNonOpaqueExistentialComponent(CanArchetypeType openedArchetype,
                                     LValueTypeData typeData)
      : LogicalPathComponent(typeData, OpenNonOpaqueExistentialKind),
        OpenedArchetype(openedArchetype) {}

   virtual bool isLoadingPure() const override { return true; }

   Optional<AccessedStorage> getAccessedStorage() const override {
      return None;
   }

   RValue get(PILGenFunction &SGF, PILLocation loc,
              ManagedValue base, SGFContext c) && override {
      auto refType = base.getType().getObjectType();
      auto &TL = SGF.getTypeLowering(refType);

      // Load the original value if necessary.
      auto result = base.getType().isAddress()
                    ? SGF.emitLoad(loc, base.getValue(), TL,
                                   SGFContext(), IsNotTake)
                    : base;

      assert(refType.isAnyExistentialType() &&
             "base for open existential component must be an existential");
      ManagedValue ref;
      if (refType.is<ExistentialMetatypeType>()) {
         assert(refType.getPreferredExistentialRepresentation()
                == ExistentialRepresentation::Metatype);
         ref = ManagedValue::forUnmanaged(
            SGF.B.createOpenExistentialMetatype(loc,
                                                result.getUnmanagedValue(),
                                                getTypeOfRValue()));
      } else {
         assert(refType.getPreferredExistentialRepresentation()
                == ExistentialRepresentation::Class);
         ref = SGF.B.createOpenExistentialRef(loc, result, getTypeOfRValue());
      }

      return RValue(SGF, loc, getSubstFormalType(), ref);
   }

   void set(PILGenFunction &SGF, PILLocation loc,
            ArgumentSource &&value, ManagedValue base) && override {
      auto payload = std::move(value).getAsSingleValue(SGF).forward(SGF);

      SmallVector<InterfaceConformanceRef, 2> conformances;
      for (auto proto : OpenedArchetype->getConformsTo())
         conformances.push_back(InterfaceConformanceRef(proto));

      PILValue ref;
      if (base.getType().is<ExistentialMetatypeType>()) {
         ref = SGF.B.createInitExistentialMetatype(
            loc,
            payload,
            base.getType().getObjectType(),
            SGF.getAstContext().AllocateCopy(conformances));
      } else {
         assert(getSubstFormalType()->isBridgeableObjectType());
         ref = SGF.B.createInitExistentialRef(
            loc,
            base.getType().getObjectType(),
            getSubstFormalType(),
            payload,
            SGF.getAstContext().AllocateCopy(conformances));
      }

      auto &TL = SGF.getTypeLowering(base.getType());
      SGF.emitSemanticStore(loc, ref,
                            base.getValue(), TL, IsNotInitialization);
   }

   std::unique_ptr<LogicalPathComponent>
   clone(PILGenFunction &SGF, PILLocation loc) const override {
      LogicalPathComponent *clone =
         new OpenNonOpaqueExistentialComponent(OpenedArchetype, getTypeData());
      return std::unique_ptr<LogicalPathComponent>(clone);
   }

   void dump(raw_ostream &OS, unsigned indent) const override {
      OS.indent(indent) << "OpenNonOpaqueExistentialComponent("
                        << OpenedArchetype << ", ...)\n";
   }
};

/// A physical path component which returns a literal address.
class ValueComponent : public PhysicalPathComponent {
   ManagedValue Value;
   Optional<PILAccessEnforcement> Enforcement;
   bool IsRValue;
public:
   ValueComponent(ManagedValue value,
                  Optional<PILAccessEnforcement> enforcement,
                  LValueTypeData typeData,
                  bool isRValue = false) :
      PhysicalPathComponent(typeData, ValueKind),
      Value(value),
      Enforcement(enforcement),
      IsRValue(isRValue) {
      assert(IsRValue || value.getType().isAddress());
   }

   virtual bool isLoadingPure() const override { return true; }

   ManagedValue project(PILGenFunction &SGF, PILLocation loc,
                        ManagedValue base) && override {
      assert(!base && "value component must be root of lvalue path");

      if (!Enforcement)
         return Value;

      PILValue addr = Value.getLValueAddress();
      addr = enterAccessScope(SGF, loc, addr, getTypeData(),
                              getAccessKind(), *Enforcement);

      return ManagedValue::forLValue(addr);
   }

   bool isRValue() const override {
      return IsRValue;
   }

   void dump(raw_ostream &OS, unsigned indent) const override {
      OS << "ValueComponent(";
      if (IsRValue) OS << "rvalue, ";
      if (Enforcement) {
         OS << getPILAccessEnforcementName(*Enforcement);
      } else {
         OS << "unenforced";
      }
      OS << "):\n";
      Value.dump(OS, indent + 2);
   }
};
} // end anonymous namespace

static bool isReadNoneFunction(const Expr *e) {
   // If this is a curried call to an integer literal conversion operations, then
   // we can "safely" assume it is readnone (btw, yes this is totally gross).
   // This is better to be attribute driven, a la rdar://15587352.
   if (auto *dre = dyn_cast<DeclRefExpr>(e)) {
      DeclName name = dre->getDecl()->getFullName();
      return (name.getArgumentNames().size() == 1 &&
              name.getBaseName() == DeclBaseName::createConstructor() &&
              !name.getArgumentNames()[0].empty() &&
              (name.getArgumentNames()[0].str() == "integerLiteral" ||
               name.getArgumentNames()[0].str() == "_builtinIntegerLiteral"));
   }

   // Look through DotSyntaxCallExpr, since the literal functions are curried.
   if (auto *CRCE = dyn_cast<ConstructorRefCallExpr>(e))
      return isReadNoneFunction(CRCE->getFn());

   return false;
}


/// Given two expressions used as indexes to the same SubscriptDecl (and thus
/// are guaranteed to have the same Ast type) check to see if they are going to
/// produce the same value.
static bool areCertainlyEqualIndices(const Expr *e1, const Expr *e2) {
   if (e1->getKind() != e2->getKind()) return false;

   // Look through ParenExpr's.
   if (auto *pe1 = dyn_cast<ParenExpr>(e1)) {
      auto *pe2 = cast<ParenExpr>(e2);
      return areCertainlyEqualIndices(pe1->getSubExpr(), pe2->getSubExpr());
   }

   // Calls are identical if the callee and operands are identical and we know
   // that the call is something that is "readnone".
   if (auto *ae1 = dyn_cast<ApplyExpr>(e1)) {
      auto *ae2 = cast<ApplyExpr>(e2);
      return areCertainlyEqualIndices(ae1->getFn(), ae2->getFn()) &&
             areCertainlyEqualIndices(ae1->getArg(), ae2->getArg()) &&
             isReadNoneFunction(ae1->getFn());
   }

   // TypeExpr's that produce the same metatype type are identical.
   if (isa<TypeExpr>(e1))
      return true;

   if (auto *dre1 = dyn_cast<DeclRefExpr>(e1)) {
      auto *dre2 = cast<DeclRefExpr>(e2);
      return dre1->getDecl() == dre2->getDecl() &&
             dre1->getType()->isEqual(dre2->getType());
   }

   // Compare a variety of literals.
   if (auto *il1 = dyn_cast<IntegerLiteralExpr>(e1)) {
      const auto &val1 = il1->getValue();
      const auto &val2 = cast<IntegerLiteralExpr>(e2)->getValue();
      // If the integers are arbitrary-precision, their bit-widths may differ,
      // but only if they represent different values.
      return val1.getBitWidth() == val2.getBitWidth() && val1 == val2;
   }
   if (auto *il1 = dyn_cast<FloatLiteralExpr>(e1))
      return il1->getValue().bitwiseIsEqual(
         cast<FloatLiteralExpr>(e2)->getValue());
   if (auto *bl1 = dyn_cast<BooleanLiteralExpr>(e1))
      return bl1->getValue() == cast<BooleanLiteralExpr>(e2)->getValue();
   if (auto *sl1 = dyn_cast<StringLiteralExpr>(e1))
      return sl1->getValue() == cast<StringLiteralExpr>(e2)->getValue();

   // Compare tuple expressions.
   if (auto *te1 = dyn_cast<TupleExpr>(e1)) {
      auto *te2 = cast<TupleExpr>(e2);

      // Easy checks: # of elements, trailing closures, element names.
      if (te1->getNumElements() != te2->getNumElements() ||
          te1->hasTrailingClosure() != te2->hasTrailingClosure() ||
          te1->getElementNames() != te2->getElementNames()) {
         return false;
      }

      for (unsigned i = 0, n = te1->getNumElements(); i != n; ++i) {
         if (!areCertainlyEqualIndices(te1->getElement(i), te2->getElement(i)))
            return false;
      }

      return true;
   }

   // Otherwise, we have no idea if they are identical.
   return false;
}

static LValueOptions getBaseOptions(LValueOptions options,
                                    AccessStrategy strategy) {
   return (strategy.getKind() == AccessStrategy::Storage
           ? options.forProjectedBaseLValue()
           : options.forComputedBaseLValue());
}

static ArgumentSource emitBaseValueForAccessor(PILGenFunction &SGF,
                                               PILLocation loc, LValue &&dest,
                                               CanType baseFormalType,
                                               PILDeclRef accessor);

static SGFAccessKind getBaseAccessKind(PILGenModule &SGM,
                                       AbstractStorageDecl *member,
                                       SGFAccessKind accessKind,
                                       AccessStrategy strategy,
                                       CanType baseFormalType);

namespace {
/// A helper class for implementing components that involve accessing
/// storage.
template <class Base>
class AccessComponent : public Base {
protected:
   // The VarDecl or SubscriptDecl being get/set.
   AbstractStorageDecl *Storage;

   /// The subscript index expression.  Useless
   Expr *IndexExprForDiagnostics;
   PreparedArguments Indices;

   /// Ast type of the base expression, in case the accessor call
   /// requires re-abstraction.
   CanType BaseFormalType;

   struct AccessorArgs {
      ArgumentSource base;
      PreparedArguments Indices;
   };

   /// Returns a tuple of RValues holding the accessor value, base (retained if
   /// necessary), and subscript arguments, in that order.
   AccessorArgs
   prepareAccessorArgs(PILGenFunction &SGF, PILLocation loc,
                       ManagedValue base, PILDeclRef accessor) &&
   {
      AccessorArgs result;
      if (base) {
         // Borrow the base, because we may need it again to invoke other
         // accessors.
         result.base = SGF.prepareAccessorBaseArg(loc,
                                                  base.formalAccessBorrow(SGF, loc),
                                                  BaseFormalType,
                                                  accessor);
      }

      if (!Indices.isNull())
         result.Indices = std::move(Indices);

      return result;
   }

   AccessComponent(PathComponent::KindTy kind,
                   AbstractStorageDecl *storage,
                   CanType baseFormalType,
                   LValueTypeData typeData,
                   Expr *indexExprForDiagnostics,
                   PreparedArguments &&indices)
      : Base(typeData, kind), Storage(storage),
        IndexExprForDiagnostics(indexExprForDiagnostics),
        Indices(std::move(indices)),
        BaseFormalType(baseFormalType)
   {
   }

   AccessComponent(const AccessComponent &copied,
                   PILGenFunction &SGF,
                   PILLocation loc)
      : Base(copied.getTypeData(), copied.getKind()),
        Storage(copied.Storage),
        IndexExprForDiagnostics(copied.IndexExprForDiagnostics),
        Indices(copied.Indices.copy(SGF, loc)) ,
        BaseFormalType(copied.BaseFormalType) {}

   bool doesAccessorMutateSelf(PILGenFunction &SGF,
                               PILDeclRef accessor) const {
      auto accessorSelf = SGF.SGM.Types.getConstantSelfParameter(
         SGF.getTypeExpansionContext(), accessor);
      return accessorSelf.getInterfaceType()
             && accessorSelf.isIndirectMutating();
   }

   void printBase(raw_ostream &OS, unsigned indent, StringRef name) const {
      OS.indent(indent) << name << "(" << Storage->getBaseName() << ")";
      if (IndexExprForDiagnostics) {
         OS << " subscript_index:\n";
         IndexExprForDiagnostics->dump(OS, 2);
      }
      OS << '\n';
   }
};

/// A helper class for implementing a component that involves
/// calling accessors.
template <class Base>
class AccessorBasedComponent : public AccessComponent<Base> {
   using super = AccessComponent<Base>;

protected:
   PILDeclRef Accessor;
   bool IsSuper;
   bool IsDirectAccessorUse;
   bool IsOnSelfParameter;
   SubstitutionMap Substitutions;

public:
   AccessorBasedComponent(PathComponent::KindTy kind,
                          AbstractStorageDecl *decl, PILDeclRef accessor,
                          bool isSuper, bool isDirectAccessorUse,
                          SubstitutionMap substitutions,
                          CanType baseFormalType, LValueTypeData typeData,
                          Expr *indexExprForDiagnostics,
                          PreparedArguments &&indices,
                          bool isOnSelfParameter = false)
      : super(kind, decl, baseFormalType, typeData, indexExprForDiagnostics,
              std::move(indices)),
        Accessor(accessor), IsSuper(isSuper),
        IsDirectAccessorUse(isDirectAccessorUse),
        IsOnSelfParameter(isOnSelfParameter), Substitutions(substitutions) {}

   AccessorBasedComponent(const AccessorBasedComponent &copied,
                          PILGenFunction &SGF,
                          PILLocation loc)
      : super(copied, SGF, loc),
        Accessor(copied.Accessor),
        IsSuper(copied.IsSuper),
        IsDirectAccessorUse(copied.IsDirectAccessorUse),
        IsOnSelfParameter(copied.IsOnSelfParameter),
        Substitutions(copied.Substitutions) {}

   AccessorDecl *getAccessorDecl() const {
      return cast<AccessorDecl>(Accessor.getFuncDecl());
   }
};

class GetterSetterComponent
   : public AccessorBasedComponent<LogicalPathComponent> {
public:

   GetterSetterComponent(AbstractStorageDecl *decl,
                         PILDeclRef accessor,
                         bool isSuper, bool isDirectAccessorUse,
                         SubstitutionMap substitutions,
                         CanType baseFormalType,
                         LValueTypeData typeData,
                         Expr *subscriptIndexExpr,
                         PreparedArguments &&indices,
                         bool isOnSelfParameter)
      : AccessorBasedComponent(GetterSetterKind, decl, accessor, isSuper,
                               isDirectAccessorUse, substitutions,
                               baseFormalType, typeData, subscriptIndexExpr,
                               std::move(indices), isOnSelfParameter)
   {
      assert(getAccessorDecl()->isGetterOrSetter());
   }

   GetterSetterComponent(const GetterSetterComponent &copied,
                         PILGenFunction &SGF,
                         PILLocation loc)
      : AccessorBasedComponent(copied, SGF, loc)
   {
   }

   bool hasPropertyWrapper() const {
      if (auto *VD = dyn_cast<VarDecl>(Storage)) {
         // If this is not a wrapper property that can be initialized from
         // a value of the wrapped type, we can't perform the initialization.
         auto wrapperInfo = VD->getPropertyWrapperBackingPropertyInfo();
         if (!wrapperInfo.initializeFromOriginal)
            return false;

         // If we have a nonmutating setter on a value type, the call
         // captures all of 'self' and we cannot rewrite an assignment
         // into an initialization.
         if (!VD->isSetterMutating() &&
             VD->getDeclContext()->getSelfNominalTypeDecl() &&
             VD->isInstanceMember() &&
             !VD->getDeclContext()->getDeclaredInterfaceType()
                ->hasReferenceSemantics()) {
            return false;
         }

         return true;
      }

      return false;
   }

   void emitAssignWithSetter(PILGenFunction &SGF, PILLocation loc,
                             LValue &&dest, ArgumentSource &&value) {
      assert(getAccessorDecl()->isSetter());
      PILDeclRef setter = Accessor;

      // Pull everything out of this that we'll need, because we're
      // about to modify the LValue and delete this component.
      auto subs = this->Substitutions;
      bool isSuper = this->IsSuper;
      bool isDirectAccessorUse = this->IsDirectAccessorUse;
      auto indices = std::move(this->Indices);
      auto baseFormalType = this->BaseFormalType;
      bool isOnSelfParameter = this->IsOnSelfParameter;

      // Drop this component from the l-value.
      dest.dropLastComponent(*this);

      return emitAssignWithSetter(SGF, loc, std::move(dest), baseFormalType,
                                  isSuper, setter, isDirectAccessorUse, subs,
                                  std::move(indices), std::move(value),
                                  isOnSelfParameter);
   }

   static void emitAssignWithSetter(PILGenFunction &SGF, PILLocation loc,
                                    LValue &&baseLV, CanType baseFormalType,
                                    bool isSuper, PILDeclRef setter,
                                    bool isDirectAccessorUse,
                                    SubstitutionMap subs,
                                    PreparedArguments &&indices,
                                    ArgumentSource &&value,
                                    bool isSelfParameter) {
      ArgumentSource self = [&] {
         if (!baseLV.isValid()) {
            return ArgumentSource();
         } else if (computeSelfParam(cast<FuncDecl>(setter.getDecl()))
            .getParameterFlags().isInOut()) {
            return ArgumentSource(loc, std::move(baseLV));
         } else {
            return emitBaseValueForAccessor(SGF, loc, std::move(baseLV),
                                            baseFormalType, setter);
         }
      }();

      return SGF.emitSetAccessor(loc, setter, subs, std::move(self), isSuper,
                                 isDirectAccessorUse, std::move(indices),
                                 std::move(value), isSelfParameter);
   }

   /// Determine whether the backing variable for the given
   /// property (that has a wrapper) is visible from the
   /// current context.
   static bool isBackingVarVisible(VarDecl *field,
                                   DeclContext *fromDC){
      VarDecl *backingVar = field->getPropertyWrapperBackingProperty();
      return backingVar->isAccessibleFrom(fromDC);
   }

   void set(PILGenFunction &SGF, PILLocation loc,
            ArgumentSource &&value, ManagedValue base) && override {
      assert(getAccessorDecl()->isSetter());
      PILDeclRef setter = Accessor;

      if (hasPropertyWrapper() && IsOnSelfParameter &&
          isBackingVarVisible(cast<VarDecl>(Storage),
                              SGF.FunctionDC)) {
         // This is wrapped property. Instead of emitting a setter, emit an
         // assign_by_wrapper with the allocating initializer function and the
         // setter function as arguments. DefiniteInitializtion will then decide
         // between the two functions, depending if it's an initialization or a
         // re-assignment.
         //
         VarDecl *field = cast<VarDecl>(Storage);
         VarDecl *backingVar = field->getPropertyWrapperBackingProperty();
         assert(backingVar);
         CanType ValType =
            SGF.F.mapTypeIntoContext(backingVar->getInterfaceType())
               ->getCanonicalType();
         // TODO: revist minimal
         PILType varStorageType = SGF.SGM.Types.getSubstitutedStorageType(
            TypeExpansionContext::minimal(), backingVar, ValType);
         auto typeData =
            getLogicalStorageTypeData(SGF.getTypeExpansionContext(), SGF.SGM,
                                      getTypeData().AccessKind, ValType);

         // Get the address of the storage property.
         ManagedValue proj;
         if (BaseFormalType->mayHaveSuperclass()) {
            RefElementComponent REC(backingVar, LValueOptions(), varStorageType,
                                    typeData);
            proj = std::move(REC).project(SGF, loc, base);
         } else {
            assert(BaseFormalType->getStructOrBoundGenericStruct());
            StructElementComponent SEC(backingVar, varStorageType, typeData);
            proj = std::move(SEC).project(SGF, loc, base);
         }

         // The property wrapper backing initializer forms an instance of
         // the backing storage type from a wrapped value.
         PILDeclRef initConstant(
            field, PILDeclRef::Kind::PropertyWrapperBackingInitializer);
         PILValue initFRef = SGF.emitGlobalFunctionRef(loc, initConstant);

         SubstitutionMap initSubs;
         if (auto genericSig = field->getInnermostDeclContext()
            ->getGenericSignatureOfContext()) {
            initSubs = SubstitutionMap::get(
               genericSig,
               [&](SubstitutableType *type) {
                  if (auto gp = type->getAs<GenericTypeParamType>()) {
                     return SGF.F.mapTypeIntoContext(gp);
                  }

                  return Type(type);
               },
               LookUpConformanceInModule(SGF.SGM.M.getPolarphpModule()));
         }

         PartialApplyInst *initPAI =
            SGF.B.createPartialApply(loc, initFRef,
                                     initSubs, ArrayRef<PILValue>(),
                                     ParameterConvention::Direct_Guaranteed);
         ManagedValue initFn = SGF.emitManagedRValueWithCleanup(initPAI);

         // Create the allocating setter function. It captures the base address.
         auto setterInfo =
            SGF.getConstantInfo(SGF.getTypeExpansionContext(), setter);
         PILValue setterFRef;
         // @todo
//         if (setter.hasDecl() && setter.getDecl()->isObjCDynamic()) {
//            auto methodTy = PILType::getPrimitiveObjectType(
//               SGF.SGM.Types.getConstantFunctionType(SGF.getTypeExpansionContext(),
//                                                     setter));
//            setterFRef = SGF.B.createObjCMethod(
//               loc, base.getValue(), setter, methodTy);
//         } else
         setterFRef = SGF.emitGlobalFunctionRef(loc, setter, setterInfo);
         CanPILFunctionType setterTy = setterFRef->getType().castTo<PILFunctionType>();
         PILFunctionConventions setterConv(setterTy, SGF.SGM.M);

         PILValue capturedBase;
         unsigned argIdx = setterConv.getNumPILArguments() - 1;
         if (setterConv.getPILArgumentConvention(argIdx).isInoutConvention()) {
            capturedBase = base.getValue();
         } else {
            capturedBase = base.copy(SGF, loc).forward(SGF);
         }

         PartialApplyInst *setterPAI =
            SGF.B.createPartialApply(loc, setterFRef,
                                     Substitutions, { capturedBase },
                                     ParameterConvention::Direct_Guaranteed);
         ManagedValue setterFn = SGF.emitManagedRValueWithCleanup(setterPAI);

         // Create the assign_by_wrapper with the allocator and setter.
         assert(value.isRValue());
         ManagedValue Mval = std::move(value).asKnownRValue(SGF).
            getAsSingleValue(SGF, loc);
         SGF.B.createAssignByWrapper(loc, Mval.forward(SGF), proj.forward(SGF),
                                     initFn.getValue(), setterFn.getValue(),
                                     AssignOwnershipQualifier::Unknown);
         return;
      }

      FormalEvaluationScope scope(SGF);
      // Pass in just the setter.
      auto args =
         std::move(*this).prepareAccessorArgs(SGF, loc, base, setter);

      return SGF.emitSetAccessor(loc, setter, Substitutions,
                                 std::move(args.base), IsSuper,
                                 IsDirectAccessorUse, std::move(args.Indices),
                                 std::move(value), IsOnSelfParameter);
   }

   ManagedValue project(PILGenFunction &SGF, PILLocation loc,
                        ManagedValue base) && override {
      assert(isReadAccess(getAccessKind()) &&
             "shouldn't be using this path to call modify");
      return std::move(*this).LogicalPathComponent::project(SGF, loc, base);
   }

   RValue get(PILGenFunction &SGF, PILLocation loc,
              ManagedValue base, SGFContext c) && override {
      assert(getAccessorDecl()->isGetter());
      PILDeclRef getter = Accessor;

      FormalEvaluationScope scope(SGF);

      auto args =
         std::move(*this).prepareAccessorArgs(SGF, loc, base, getter);

      return SGF.emitGetAccessor(
         loc, getter, Substitutions, std::move(args.base), IsSuper,
         IsDirectAccessorUse, std::move(args.Indices), c, IsOnSelfParameter);
   }

   std::unique_ptr<LogicalPathComponent>
   clone(PILGenFunction &SGF, PILLocation loc) const override {
      LogicalPathComponent *clone = new GetterSetterComponent(*this, SGF, loc);
      return std::unique_ptr<LogicalPathComponent>(clone);
   }

   void dump(raw_ostream &OS, unsigned indent) const override {
      printBase(OS, indent, "GetterSetterComponent");
   }

   /// Compare 'this' lvalue and the 'rhs' lvalue (which is guaranteed to have
   /// the same dynamic PathComponent type as the receiver) to see if they are
   /// identical.  If so, there is a conflicting writeback happening, so emit a
   /// diagnostic.
   Optional<AccessedStorage> getAccessedStorage() const override {
      return AccessedStorage{Storage, IsSuper,
                             Indices.isNull() ? nullptr : &Indices,
                             IndexExprForDiagnostics };
   }
};

class MaterializeToTemporaryComponent final
   : public AccessComponent<LogicalPathComponent> {
   SubstitutionMap Substitutions;
   AccessStrategy ReadStrategy;
   AccessStrategy WriteStrategy;
   LValueOptions Options;
   bool IsSuper;
   bool IsOnSelfParameter;

public:
   MaterializeToTemporaryComponent(AbstractStorageDecl *storage,
                                   bool isSuper, SubstitutionMap subs,
                                   LValueOptions options,
                                   AccessStrategy readStrategy,
                                   AccessStrategy writeStrategy,
                                   CanType baseFormalType,
                                   LValueTypeData typeData,
                                   Expr *indexExprForDiagnostics,
                                   PreparedArguments &&indices,
                                   bool isOnSelfParameter)
      : AccessComponent(MaterializeToTemporaryKind, storage, baseFormalType,
                        typeData, indexExprForDiagnostics, std::move(indices)),
        Substitutions(subs),
        ReadStrategy(readStrategy), WriteStrategy(writeStrategy),
        Options(options), IsSuper(isSuper), IsOnSelfParameter(isOnSelfParameter)
   {}


   std::unique_ptr<LogicalPathComponent>
   clone(PILGenFunction &SGF, PILLocation loc) const override {
      PreparedArguments clonedIndices = Indices.copy(SGF, loc);

      LogicalPathComponent *clone =
         new MaterializeToTemporaryComponent(Storage, IsSuper, Substitutions,
                                             Options,
                                             ReadStrategy, WriteStrategy,
                                             BaseFormalType, getTypeData(),
                                             IndexExprForDiagnostics,
                                             std::move(clonedIndices),
                                             IsOnSelfParameter);
      return std::unique_ptr<LogicalPathComponent>(clone);
   }

   RValue get(PILGenFunction &SGF, PILLocation loc,
              ManagedValue base, SGFContext C) && override {
      LValue lv = std::move(*this).prepareLValue(SGF, loc, base,
                                                 SGFAccessKind::OwnedObjectRead,
                                                 ReadStrategy);
      return SGF.emitLoadOfLValue(loc, std::move(lv), C);
   }

   void set(PILGenFunction &SGF, PILLocation loc,
            ArgumentSource &&value, ManagedValue base) && override {
      LValue lv = std::move(*this).prepareLValue(SGF, loc, base,
                                                 SGFAccessKind::Write,
                                                 WriteStrategy);
      return SGF.emitAssignToLValue(loc, std::move(value), std::move(lv));
   }

   Optional<AccessedStorage> getAccessedStorage() const override {
      return AccessedStorage{Storage, IsSuper,
                             Indices.isNull() ? nullptr : &Indices,
                             IndexExprForDiagnostics};
   }

   void dump(raw_ostream &OS, unsigned indent) const override {
      OS.indent(indent) << "MaterializeToTemporaryComponent";
   }

private:
   LValue prepareLValue(PILGenFunction &SGF, PILLocation loc,
                        ManagedValue base, SGFAccessKind accessKind,
                        AccessStrategy strategy) && {
      LValue lv = [&] {
         if (!base) return LValue();
         auto baseAccessKind =
            getBaseAccessKind(SGF.SGM, Storage, accessKind, strategy,
                              BaseFormalType);
         return LValue::forValue(baseAccessKind, base, BaseFormalType);
      }();

      if (auto subscript = dyn_cast<SubscriptDecl>(Storage)) {
         lv.addMemberSubscriptComponent(SGF, loc, subscript, Substitutions,
                                        Options, IsSuper, accessKind, strategy,
                                        getSubstFormalType(),
                                        std::move(Indices),
                                        IndexExprForDiagnostics);
      } else {
         auto var = cast<VarDecl>(Storage);
         if (base) {
            lv.addMemberVarComponent(SGF, loc, var, Substitutions, Options,
                                     IsSuper, accessKind, strategy,
                                     getSubstFormalType());
         } else {
            lv.addNonMemberVarComponent(SGF, loc, var, Substitutions, Options,
                                        accessKind, strategy,
                                        getSubstFormalType());
         }
      }

      return lv;
   }
};

/// A physical component which involves calling addressors.
class AddressorComponent
   : public AccessorBasedComponent<PhysicalPathComponent> {
   PILType SubstFieldType;
public:
   AddressorComponent(AbstractStorageDecl *decl, PILDeclRef accessor,
                      bool isSuper, bool isDirectAccessorUse,
                      SubstitutionMap substitutions,
                      CanType baseFormalType, LValueTypeData typeData,
                      PILType substFieldType,
                      Expr *indexExprForDiagnostics,
                      PreparedArguments &&indices, bool isOnSelfParameter)
      : AccessorBasedComponent(AddressorKind, decl, accessor, isSuper,
                               isDirectAccessorUse, substitutions,
                               baseFormalType, typeData,
                               indexExprForDiagnostics, std::move(indices),
                               isOnSelfParameter),
        SubstFieldType(substFieldType)
   {
      assert(getAccessorDecl()->isAnyAddressor());
   }

   ManagedValue project(PILGenFunction &SGF, PILLocation loc,
                        ManagedValue base) && override {
      assert(SGF.isInFormalEvaluationScope() &&
             "offsetting l-value for modification without writeback scope");

      ManagedValue addr;
      {
         FormalEvaluationScope scope(SGF);

         auto args =
            std::move(*this).prepareAccessorArgs(SGF, loc, base, Accessor);
         addr = SGF.emitAddressorAccessor(
            loc, Accessor, Substitutions, std::move(args.base), IsSuper,
            IsDirectAccessorUse, std::move(args.Indices), SubstFieldType,
            IsOnSelfParameter);
      }

      // Enter an unsafe access scope for the access.
      addr = enterAccessScope(SGF, loc, addr, getTypeData(), getAccessKind(),
                              PILAccessEnforcement::Unsafe);

      return addr;
   }

   void dump(raw_ostream &OS, unsigned indent) const override {
      printBase(OS, indent, "AddressorComponent");
   }
};

class EndApplyPseudoComponent : public WritebackPseudoComponent {
   CleanupHandle EndApplyHandle;
   AbstractStorageDecl *Storage;
   bool IsSuper;
   PreparedArguments PeekedIndices;
   Expr *IndexExprForDiagnostics;
public:
   EndApplyPseudoComponent(const LValueTypeData &typeData,
                           CleanupHandle endApplyHandle,
                           AbstractStorageDecl *storage,
                           bool isSuper,
                           PreparedArguments &&peekedIndices,
                           Expr *indexExprForDiagnostics)
      : WritebackPseudoComponent(typeData),
        EndApplyHandle(endApplyHandle),
        Storage(storage), IsSuper(isSuper),
        PeekedIndices(std::move(peekedIndices)),
        IndexExprForDiagnostics(indexExprForDiagnostics) {}

private:
   void writeback(PILGenFunction &SGF, PILLocation loc,
                  ManagedValue base,
                  MaterializedLValue materialized,
                  bool isFinal) override {
      // Just let the cleanup get emitted normally if the writeback is for
      // an unwind.
      if (!isFinal) return;

      SGF.Cleanups.popAndEmitCleanup(EndApplyHandle, CleanupLocation::get(loc),
                                     NotForUnwind);
   }

   void dump(raw_ostream &OS, unsigned indent) const override {
      OS.indent(indent) << "EndApplyPseudoComponent";
   }

   Optional<AccessedStorage> getAccessedStorage() const override {
      return AccessedStorage{Storage, IsSuper,
                             PeekedIndices.isNull() ? nullptr : &PeekedIndices,
                             IndexExprForDiagnostics};
   }
};
}

static void pushEndApplyWriteback(PILGenFunction &SGF, PILLocation loc,
                                  CleanupHandle endApplyHandle,
                                  LValueTypeData typeData,
                                  ManagedValue base = ManagedValue(),
                                  AbstractStorageDecl *storage = nullptr,
                                  bool isSuper = false,
                                  PreparedArguments &&indices
                                  = PreparedArguments(),
                                  Expr *indexExprForDiagnostics = nullptr) {
   std::unique_ptr<LogicalPathComponent>
      component(new EndApplyPseudoComponent(typeData, endApplyHandle,
                                            storage, isSuper, std::move(indices),
                                            indexExprForDiagnostics));
   pushWriteback(SGF, loc, std::move(component), /*for diagnostics*/ base,
                 MaterializedLValue());
}

namespace {

/// A physical component which involves calling coroutine accessors.
class CoroutineAccessorComponent
   : public AccessorBasedComponent<PhysicalPathComponent> {
public:
   CoroutineAccessorComponent(AbstractStorageDecl *decl, PILDeclRef accessor,
                              bool isSuper, bool isDirectAccessorUse,
                              SubstitutionMap substitutions,
                              CanType baseFormalType, LValueTypeData typeData,
                              Expr *indexExprForDiagnostics,
                              PreparedArguments &&indices,
                              bool isOnSelfParameter)
      : AccessorBasedComponent(
      CoroutineAccessorKind, decl, accessor, isSuper,
      isDirectAccessorUse, substitutions, baseFormalType, typeData,
      indexExprForDiagnostics, std::move(indices), isOnSelfParameter) {}

   using AccessorBasedComponent::AccessorBasedComponent;

   ManagedValue project(PILGenFunction &SGF, PILLocation loc,
                        ManagedValue base) && override {
      assert(SGF.isInFormalEvaluationScope() &&
             "offsetting l-value for modification without writeback scope");

      ManagedValue result;

      auto args =
         std::move(*this).prepareAccessorArgs(SGF, loc, base, Accessor);
      auto peekedIndices = args.Indices.copyForDiagnostics();
      SmallVector<ManagedValue, 4> yields;
      auto endApplyHandle = SGF.emitCoroutineAccessor(
         loc, Accessor, Substitutions, std::move(args.base), IsSuper,
         IsDirectAccessorUse, std::move(args.Indices), yields,
         IsOnSelfParameter);

      // Push a writeback that ends the access.
      pushEndApplyWriteback(SGF, loc, endApplyHandle, getTypeData(),
                            base, Storage, IsSuper, std::move(peekedIndices),
                            IndexExprForDiagnostics);

      auto decl = cast<AccessorDecl>(Accessor.getFuncDecl());

      // 'modify' always returns an address of the right type.
      if (decl->getAccessorKind() == AccessorKind::Modify) {
         assert(yields.size() == 1);
         return yields[0];
      }

      // 'read' returns a borrowed r-value, which might or might not be
      // an address of the right type.

      // Use the yield value directly if it's the right type.
      if (!getOrigFormalType().isTuple()) {
         assert(yields.size() == 1);
         auto value = yields[0];
         if (value.getType().isAddress() ||
             !isReadAccessResultAddress(getAccessKind()))
            return value;
      }

      // Otherwise, we need to make a temporary.
      // TODO: build a scalar tuple if possible.
      auto temporary =
         SGF.emitTemporary(loc, SGF.getTypeLowering(getTypeOfRValue()));
      auto yieldsAsArray = llvm::makeArrayRef(yields);
      copyBorrowedYieldsIntoTemporary(SGF, loc, yieldsAsArray,
                                      getOrigFormalType(), getSubstFormalType(),
                                      temporary.get());
      assert(yieldsAsArray.empty() && "didn't claim all yields");
      return temporary->getManagedAddress();
   }

   void dump(raw_ostream &OS, unsigned indent) const override {
      printBase(OS, indent, "CoroutineAccessorComponent");
   }
};
}

static ManagedValue
makeBaseConsumableMaterializedRValue(PILGenFunction &SGF,
                                     PILLocation loc, ManagedValue base) {
   if (base.isLValue()) {
      auto tmp = SGF.emitTemporaryAllocation(loc, base.getType());
      SGF.B.createCopyAddr(loc, base.getValue(), tmp,
                           IsNotTake, IsInitialization);
      return SGF.emitManagedBufferWithCleanup(tmp);
   }

   bool isBorrowed = base.isPlusZeroRValueOrTrivial()
                     && !base.getType().isTrivial(SGF.F);
   if (!base.getType().isAddress() || isBorrowed) {
      auto tmp = SGF.emitTemporaryAllocation(loc, base.getType());
      if (isBorrowed)
         base.copyInto(SGF, loc, tmp);
      else
         base.forwardInto(SGF, loc, tmp);
      return SGF.emitManagedBufferWithCleanup(tmp);
   }

   return base;
}

static ManagedValue
emitUpcastToKeyPath(PILGenFunction &SGF, PILLocation loc,
                    KeyPathTypeKind typeKind, ManagedValue keyPath) {
   if (typeKind == KPTK_KeyPath) return keyPath;
   assert(typeKind == KPTK_WritableKeyPath ||
          typeKind == KPTK_ReferenceWritableKeyPath);

   auto derivedKeyPathTy = keyPath.getType().castTo<BoundGenericType>();
   auto baseKeyPathTy =
      BoundGenericType::get(SGF.getAstContext().getKeyPathDecl(),
                            Type(), derivedKeyPathTy->getGenericArgs())
         ->getCanonicalType();
   return SGF.B.createUpcast(loc, keyPath,
                             PILType::getPrimitiveObjectType(baseKeyPathTy));
}

namespace {
class LogicalKeyPathApplicationComponent final
   : public LogicalPathComponent {
   KeyPathTypeKind TypeKind;
   ManagedValue KeyPath;
   Type BaseFormalType;
public:
   LogicalKeyPathApplicationComponent(LValueTypeData typeData,
                                      KeyPathTypeKind typeKind,
                                      ManagedValue keyPath,
                                      Type baseFormalType)
      : LogicalPathComponent(typeData, LogicalKeyPathApplicationKind),
        TypeKind(typeKind), KeyPath(keyPath), BaseFormalType(baseFormalType) {
      assert(isReadAccess(getAccessKind()) ||
             typeKind == KPTK_WritableKeyPath ||
             typeKind == KPTK_ReferenceWritableKeyPath);
   }

   RValue get(PILGenFunction &SGF, PILLocation loc,
              ManagedValue base, SGFContext C) && override {
      assert(isReadAccess(getAccessKind()));
      FuncDecl *projectFn;

      auto keyPathValue = KeyPath;

      SmallVector<Type, 2> typeArgs;
      typeArgs.push_back(BaseFormalType);
      if (TypeKind == KPTK_AnyKeyPath) {
         projectFn = SGF.getAstContext().getGetAtAnyKeyPath();
      } else if (TypeKind == KPTK_PartialKeyPath) {
         projectFn = SGF.getAstContext().getGetAtPartialKeyPath();
      } else if (TypeKind == KPTK_KeyPath ||
                 TypeKind == KPTK_WritableKeyPath ||
                 TypeKind == KPTK_ReferenceWritableKeyPath) {
         projectFn = SGF.getAstContext().getGetAtKeyPath();

         auto keyPathTy = keyPathValue.getType().castTo<BoundGenericType>();
         assert(keyPathTy->getGenericArgs().size() == 2);
         assert(keyPathTy->getGenericArgs()[0]->getCanonicalType() ==
                BaseFormalType->getCanonicalType());
         typeArgs.push_back(keyPathTy->getGenericArgs()[1]);

         keyPathValue = emitUpcastToKeyPath(SGF, loc, TypeKind, keyPathValue);
      } else {
         llvm_unreachable("bad key path kind for this component");
      }

      auto subs = SubstitutionMap::get(projectFn->getGenericSignature(),
                                       ArrayRef<Type>(typeArgs),
                                       ArrayRef<InterfaceConformanceRef>());

      base = makeBaseConsumableMaterializedRValue(SGF, loc, base);

      return SGF.emitApplyOfLibraryIntrinsic(loc, projectFn, subs,
                                             {base, keyPathValue}, C);
   }

   void set(PILGenFunction &SGF, PILLocation loc,
            ArgumentSource &&value, ManagedValue base) && override {
      assert(!isReadAccess(getAccessKind()));

      auto keyPathValue = KeyPath;
      FuncDecl *setFn;
      if (TypeKind == KPTK_WritableKeyPath) {
         setFn = SGF.getAstContext().getSetAtWritableKeyPath();
         assert(base.isLValue());
      } else if (TypeKind == KPTK_ReferenceWritableKeyPath) {
         setFn = SGF.getAstContext().getSetAtReferenceWritableKeyPath();
         base = makeBaseConsumableMaterializedRValue(SGF, loc, base);
      } else {
         llvm_unreachable("bad writable type kind");
      }

      auto keyPathTy = keyPathValue.getType().castTo<BoundGenericType>();
      auto subs = SubstitutionMap::get(setFn->getGenericSignature(),
                                       keyPathTy->getGenericArgs(),
                                       ArrayRef<InterfaceConformanceRef>());

      auto setValue =
         std::move(value).getAsSingleValue(SGF, AbstractionPattern::getOpaque());
      if (!setValue.getType().isAddress()) {
         setValue = setValue.materialize(SGF, loc);
      }

      SGF.emitApplyOfLibraryIntrinsic(loc, setFn, subs,
                                      {base, keyPathValue, setValue},
                                      SGFContext());
   }

   Optional<AccessedStorage> getAccessedStorage() const override {
      return None;
   }

   std::unique_ptr<LogicalPathComponent>
   clone(PILGenFunction &SGF, PILLocation l) const override {
      llvm_unreachable("can't be cloned");
   }

   void dump(raw_ostream &OS, unsigned indent) const override {
      OS.indent(indent) << "LogicalKeyPathApplicationComponent\n";
   }
};

/// A physical component which involves applying a key path.
class PhysicalKeyPathApplicationComponent final
   : public PhysicalPathComponent {
   KeyPathTypeKind TypeKind;
   ManagedValue KeyPath;
public:
   PhysicalKeyPathApplicationComponent(LValueTypeData typeData,
                                       KeyPathTypeKind typeKind,
                                       ManagedValue keyPath)
      : PhysicalPathComponent(typeData, PhysicalKeyPathApplicationKind),
        TypeKind(typeKind), KeyPath(keyPath) {
      assert(typeKind == KPTK_KeyPath ||
             typeKind == KPTK_WritableKeyPath ||
             typeKind == KPTK_ReferenceWritableKeyPath);
      assert(typeKind != KPTK_KeyPath || isReadAccess(getAccessKind()));
   }

   ManagedValue project(PILGenFunction &SGF, PILLocation loc,
                        ManagedValue base) && override {
      assert(SGF.isInFormalEvaluationScope() &&
             "offsetting l-value for modification without writeback scope");

      bool isRead = isReadAccess(getAccessKind());

      // Set up the base and key path values correctly.
      auto keyPathValue = KeyPath;
      if (isRead) {
         keyPathValue = emitUpcastToKeyPath(SGF, loc, TypeKind, keyPathValue);
         base = makeBaseConsumableMaterializedRValue(SGF, loc, base);
      } else if (TypeKind == KPTK_WritableKeyPath) {
         // nothing to do
      } else if (TypeKind == KPTK_ReferenceWritableKeyPath) {
         base = makeBaseConsumableMaterializedRValue(SGF, loc, base);
      } else {
         llvm_unreachable("bad combination");
      }

      PILFunction *projectFn =
         SGF.SGM.getKeyPathProjectionCoroutine(isRead, TypeKind);
      auto projectFnRef = SGF.B.createManagedFunctionRef(loc, projectFn);
      auto projectFnType = projectFn->getLoweredFunctionType();

      auto keyPathTy = keyPathValue.getType().castTo<BoundGenericType>();
      auto subs = SubstitutionMap::get(
         projectFnType->getInvocationGenericSignature(),
         keyPathTy->getGenericArgs(), {});

      auto substFnType = projectFnType->substGenericArgs(
         SGF.SGM.M, subs, SGF.getTypeExpansionContext());

      // Perform the begin_apply.
      SmallVector<ManagedValue, 1> yields;
      auto cleanup =
         SGF.emitBeginApply(loc, projectFnRef, subs, { base, keyPathValue },
                            substFnType, ApplyOptions(), yields);

      // Push an operation to do the end_apply.
      pushEndApplyWriteback(SGF, loc, cleanup, getTypeData());

      assert(yields.size() == 1);
      return yields[0];
   }

   void dump(raw_ostream &OS, unsigned indent) const override {
      OS.indent(indent) << "PhysicalKeyPathApplicationComponent\n";
   }
};
} // end anonymous namespace

RValue
TranslationPathComponent::get(PILGenFunction &SGF, PILLocation loc,
                              ManagedValue base, SGFContext c) && {
   // Inline constructor.
   RValue baseVal = [&]() -> RValue {
      // If our base is an object, just put it into an RValue and return.
      if (base.getType().isObject()) {
         return RValue(SGF, loc, getSubstFormalType(), base);
      }

      // Otherwise, load the value and put it into an RValue.
      return RValue(SGF, loc, getSubstFormalType(),
                    SGF.emitLoad(loc, base.getValue(),
                                 SGF.getTypeLowering(base.getType()),
                                 SGFContext(), IsNotTake));
   }();

   // Map the base value to its substituted representation.
   return std::move(*this).translate(SGF, loc, std::move(baseVal), c);
}

void TranslationPathComponent::set(PILGenFunction &SGF, PILLocation loc,
                                   ArgumentSource &&valueSource,
                                   ManagedValue base) && {
   assert(base.getType().isAddress() &&
          "Only support setting bases that have addresses");
   RValue value = std::move(valueSource).getAsRValue(SGF);

   // Map the value to the original pattern.
   RValue newValue = std::move(*this).untranslate(SGF, loc, std::move(value));

   // Store to the base.
   std::move(newValue).assignInto(SGF, loc, base.getValue());
}

namespace {
/// Remap an lvalue referencing a generic type to an lvalue of its
/// substituted type in a concrete context.
class OrigToSubstComponent : public TranslationPathComponent {
   AbstractionPattern OrigType;

public:
   OrigToSubstComponent(const LValueTypeData &typeData,
                        AbstractionPattern origType)
      : TranslationPathComponent(typeData, OrigToSubstKind),
        OrigType(origType)
   {}

   virtual bool isLoadingPure() const override { return true; }

   RValue untranslate(PILGenFunction &SGF, PILLocation loc,
                      RValue &&rv, SGFContext c) && override {
      return SGF.emitSubstToOrigValue(loc, std::move(rv), OrigType,
                                      getSubstFormalType(), c);
   }

   RValue translate(PILGenFunction &SGF, PILLocation loc,
                    RValue &&rv, SGFContext c) && override {
      return SGF.emitOrigToSubstValue(loc, std::move(rv), OrigType,
                                      getSubstFormalType(), c);
   }

   std::unique_ptr<LogicalPathComponent>
   clone(PILGenFunction &SGF, PILLocation loc) const override {
      LogicalPathComponent *clone
         = new OrigToSubstComponent(getTypeData(), OrigType);
      return std::unique_ptr<LogicalPathComponent>(clone);
   }

   void dump(raw_ostream &OS, unsigned indent) const override {
      OS.indent(indent) << "OrigToSubstComponent("
                        << getOrigFormalType() << ", "
                        << getSubstFormalType() << ", "
                        << getTypeOfRValue() << ")\n";
   }
};

/// Remap an lvalue referencing a concrete type to an lvalue of a
/// generically-reabstracted type.
class SubstToOrigComponent : public TranslationPathComponent {
public:
   SubstToOrigComponent(const LValueTypeData &typeData)
      : TranslationPathComponent(typeData, SubstToOrigKind)
   {}

   virtual bool isLoadingPure() const override { return true; }

   RValue untranslate(PILGenFunction &SGF, PILLocation loc,
                      RValue &&rv, SGFContext c) && override {
      return SGF.emitOrigToSubstValue(loc, std::move(rv), getOrigFormalType(),
                                      getSubstFormalType(), c);
   }

   RValue translate(PILGenFunction &SGF, PILLocation loc,
                    RValue &&rv, SGFContext c) && override {
      return SGF.emitSubstToOrigValue(loc, std::move(rv), getOrigFormalType(),
                                      getSubstFormalType(), c);
   }

   std::unique_ptr<LogicalPathComponent>
   clone(PILGenFunction &SGF, PILLocation loc) const override {
      LogicalPathComponent *clone
         = new SubstToOrigComponent(getTypeData());
      return std::unique_ptr<LogicalPathComponent>(clone);
   }

   void dump(raw_ostream &OS, unsigned indent) const override {
      OS.indent(indent) << "SubstToOrigComponent("
                        << getOrigFormalType() << ", "
                        << getSubstFormalType() << ", "
                        << getTypeOfRValue() << ")\n";
   }
};

/// Remap a weak value to Optional<T>*, or unowned pointer to T*.
class OwnershipComponent : public LogicalPathComponent {
public:
   OwnershipComponent(LValueTypeData typeData)
      : LogicalPathComponent(typeData, OwnershipKind) {
   }

   virtual bool isLoadingPure() const override { return true; }

   Optional<AccessedStorage> getAccessedStorage() const override {
      return None;
   }

   RValue get(PILGenFunction &SGF, PILLocation loc,
              ManagedValue base, SGFContext c) && override {
      assert(base && "ownership component must not be root of lvalue path");

      auto &TL = SGF.getTypeLowering(getTypeOfRValue());

      ManagedValue result;
      if (base.getType().isObject()) {
         result = SGF.emitConversionToSemanticRValue(loc, base, TL);
      } else {
         result = SGF.emitLoad(loc, base.getValue(), TL, SGFContext(),
                               IsNotTake);
      }

      return RValue(SGF, loc, getSubstFormalType(), result);
   }

   void set(PILGenFunction &SGF, PILLocation loc,
            ArgumentSource &&valueSource, ManagedValue base) && override {
      assert(base && "ownership component must not be root of lvalue path");
      auto &TL = SGF.getTypeLowering(base.getType());

      auto value = std::move(valueSource).getAsSingleValue(SGF).forward(SGF);
      SGF.emitSemanticStore(loc, value, base.getValue(), TL,
                            IsNotInitialization);
   }

   std::unique_ptr<LogicalPathComponent>
   clone(PILGenFunction &SGF, PILLocation loc) const override {
      LogicalPathComponent *clone = new OwnershipComponent(getTypeData());
      return std::unique_ptr<LogicalPathComponent>(clone);
   }

   void dump(raw_ostream &OS, unsigned indent) const override {
      OS.indent(indent) << "OwnershipComponent(...)\n";
   }
};
} // end anonymous namespace

LValue LValue::forValue(SGFAccessKind accessKind, ManagedValue value,
                        CanType substFormalType) {
   if (value.getType().isObject()) {
      LValueTypeData typeData = getValueTypeData(accessKind, substFormalType,
                                                 value.getValue());

      LValue lv;
      lv.add<ValueComponent>(value, None, typeData, /*isRValue=*/true);
      return lv;
   } else {
      // Treat an address-only value as an lvalue we only read from.
      if (!value.isLValue())
         value = ManagedValue::forLValue(value.getValue());
      return forAddress(accessKind, value, None,
                        AbstractionPattern(substFormalType), substFormalType);
   }
}

LValue LValue::forAddress(SGFAccessKind accessKind, ManagedValue address,
                          Optional<PILAccessEnforcement> enforcement,
                          AbstractionPattern origFormalType,
                          CanType substFormalType) {
   assert(address.isLValue());
   LValueTypeData typeData = {
      accessKind, origFormalType, substFormalType,
      address.getType().getAstType()
   };

   LValue lv;
   lv.add<ValueComponent>(address, enforcement, typeData);
   return lv;
}

void LValue::addMemberComponent(PILGenFunction &SGF, PILLocation loc,
                                AbstractStorageDecl *storage,
                                SubstitutionMap subs,
                                LValueOptions options,
                                bool isSuper,
                                SGFAccessKind accessKind,
                                AccessStrategy accessStrategy,
                                CanType formalRValueType,
                                PreparedArguments &&indices,
                                Expr *indexExprForDiagnostics) {
   if (auto var = dyn_cast<VarDecl>(storage)) {
      assert(indices.isNull());
      addMemberVarComponent(SGF, loc, var, subs, options, isSuper,
                            accessKind, accessStrategy, formalRValueType);
   } else {
      auto subscript = cast<SubscriptDecl>(storage);
      addMemberSubscriptComponent(SGF, loc, subscript, subs, options, isSuper,
                                  accessKind, accessStrategy, formalRValueType,
                                  std::move(indices), indexExprForDiagnostics);
   }
}

void LValue::addOrigToSubstComponent(PILType loweredSubstType) {
   loweredSubstType = loweredSubstType.getObjectType();
   assert(getTypeOfRValue() != loweredSubstType &&
          "reabstraction component is unnecessary!");

   // Peephole away complementary reabstractions.
   assert(!Path.empty() && "adding translation component to empty l-value");
   if (Path.back()->getKind() == PathComponent::SubstToOrigKind) {
      // But only if the lowered type matches exactly.
      if (Path[Path.size()-2]->getTypeOfRValue() == loweredSubstType) {
         Path.pop_back();
         return;
      }
      // TODO: combine reabstractions; this doesn't matter all that much
      // for most things, but it can be dramatically better for function
      // reabstraction.
   }

   auto substFormalType = getSubstFormalType();
   LValueTypeData typeData = {
      getAccessKind(),
      AbstractionPattern(substFormalType),
      substFormalType,
      loweredSubstType.getAstType()
   };
   add<OrigToSubstComponent>(typeData, getOrigFormalType());
}

void LValue::addSubstToOrigComponent(AbstractionPattern origType,
                                     PILType loweredSubstType) {
   loweredSubstType = loweredSubstType.getObjectType();
   assert(getTypeOfRValue() != loweredSubstType &&
          "reabstraction component is unnecessary!");

   // Peephole away complementary reabstractions.
   assert(!Path.empty() && "adding translation component to empty l-value");
   if (Path.back()->getKind() == PathComponent::OrigToSubstKind) {
      // But only if the lowered type matches exactly.
      if (Path[Path.size()-2]->getTypeOfRValue() == loweredSubstType) {
         Path.pop_back();
         return;
      }
      // TODO: combine reabstractions; this doesn't matter all that much
      // for most things, but it can be dramatically better for function
      // reabstraction.
   }

   LValueTypeData typeData = {
      getAccessKind(),
      origType,
      getSubstFormalType(),
      loweredSubstType.getAstType()
   };
   add<SubstToOrigComponent>(typeData);
}

void LValue::dump() const {
   dump(llvm::errs());
}

void LValue::dump(raw_ostream &OS, unsigned indent) const {
   for (const auto &component : *this) {
      component->dump(OS, indent);
   }
}

LValue PILGenFunction::emitLValue(Expr *e, SGFAccessKind accessKind,
                                  LValueOptions options) {
   // Some lvalue nodes (namely BindOptionalExprs) require immediate evaluation
   // of their subexpression, so we must have a writeback scope open while
   // building an lvalue.
   assert(isInFormalEvaluationScope() && "must be in a formal evaluation scope");

   LValue r = PILGenLValue(*this).visit(e, accessKind, options);
   // If the final component has an abstraction change, introduce a
   // reabstraction component.
   auto substFormalType = r.getSubstFormalType();
   auto loweredSubstType = getLoweredType(substFormalType);
   if (r.getTypeOfRValue() != loweredSubstType.getObjectType()) {
      // Logical components always re-abstract back to the substituted
      // type.
      assert(r.isLastComponentPhysical());
      r.addOrigToSubstComponent(loweredSubstType);
   }
   return r;
}

static LValue visitRecInOut(PILGenLValue &SGL, Expr *e,
                            SGFAccessKind accessKind,
                            LValueOptions options, AbstractionPattern orig) {
   auto lv = SGL.visit(e, accessKind, options);
   // If necessary, handle reabstraction with a SubstToOrigComponent that handles
   // writeback in the original representation.
   if (orig.isValid()) {
      auto &origTL = SGL.SGF.getTypeLowering(orig, e->getType()->getRValueType());
      if (lv.getTypeOfRValue() != origTL.getLoweredType().getObjectType())
         lv.addSubstToOrigComponent(orig, origTL.getLoweredType().getObjectType());
   }

   return lv;
}

// Otherwise we have a non-lvalue type (references, values, metatypes,
// etc). These act as the root of a logical lvalue.
static ManagedValue visitRecNonInOutBase(PILGenLValue &SGL, Expr *e,
                                         SGFAccessKind accessKind,
                                         LValueOptions options,
                                         AbstractionPattern orig) {
   auto &SGF = SGL.SGF;

   // For an rvalue base, apply the reabstraction (if any) eagerly, since
   // there's no need for writeback.
   if (orig.isValid()) {
      return SGF.emitRValueAsOrig(
         e, orig, SGF.getTypeLowering(orig, e->getType()->getRValueType()));
   }

   // Ok, at this point we know that re-abstraction is not required.

   SGFContext ctx;

   if (auto *dre = dyn_cast<DeclRefExpr>(e)) {
      // Any reference to "self" can be done at +0 so long as it is a direct
      // access, since we know it is guaranteed.
      //
      // TODO: it would be great to factor this even lower into PILGen to the
      // point where we can see that the parameter is +0 guaranteed.  Note that
      // this handles the case in initializers where there is actually a stack
      // allocation for it as well.
      if (isa<ParamDecl>(dre->getDecl()) &&
          dre->getDecl()->getFullName() == SGF.getAstContext().Id_self &&
          dre->getDecl()->isImplicit()) {
         ctx = SGFContext::AllowGuaranteedPlusZero;
         if (SGF.SelfInitDelegationState != PILGenFunction::NormalSelf) {
            // This needs to be inlined since there is a Formal Evaluation Scope
            // in emitRValueForDecl that causing any borrow for this LValue to be
            // popped too soon.
            auto *vd = cast<ParamDecl>(dre->getDecl());
            CanType formalRValueType = dre->getType()->getCanonicalType();
            ManagedValue selfLValue =
               SGF.emitAddressOfLocalVarDecl(dre, vd, formalRValueType,
                                             SGFAccessKind::OwnedObjectRead);
            selfLValue = SGF.emitFormalEvaluationRValueForSelfInDelegationInit(
                  e, formalRValueType,
                  selfLValue.getLValueAddress(), ctx)
               .getAsSingleValue(SGF, e);

            return selfLValue;
         }
      }

      if (auto *VD = dyn_cast<VarDecl>(dre->getDecl())) {
         // All let values are guaranteed to be held alive across their lifetime,
         // and won't change once initialized.  Any loaded value is good for the
         // duration of this expression evaluation.
         if (VD->isLet()) {
            ctx = SGFContext::AllowGuaranteedPlusZero;
         }
      }
   }

   if (SGF.SGM.Types.isIndirectPlusZeroSelfParameter(e->getType())) {
      ctx = SGFContext::AllowGuaranteedPlusZero;
   }

   ManagedValue mv = SGF.emitRValueAsSingleValue(e, ctx);
   if (mv.isPlusZeroRValueOrTrivial())
      return mv;

   // Any temporaries needed to materialize the lvalue must be destroyed when
   // at the end of the lvalue's formal evaluation scope.
   // e.g. for foo(self.bar)
   //   %self = load [copy] %ptr_self
   //   %rvalue = barGetter(%self)
   //   destroy_value %self // self must be released before calling foo.
   //   foo(%rvalue)
   PILValue value = mv.forward(SGF);
   return SGF.emitFormalAccessManagedRValueWithCleanup(CleanupLocation(e),
                                                       value);
}

LValue PILGenLValue::visitRec(Expr *e, SGFAccessKind accessKind,
                              LValueOptions options, AbstractionPattern orig) {
   // First see if we have an lvalue type. If we do, then quickly handle that and
   // return.
   if (e->getType()->is<LValueType>() || e->isSemanticallyInOutExpr()) {
      return visitRecInOut(*this, e, accessKind, options, orig);
   }

   // Otherwise we have a non-lvalue type (references, values, metatypes,
   // etc). These act as the root of a logical lvalue. Compute the root value,
   // wrap it in a ValueComponent, and return it for our caller.
   ManagedValue rv = visitRecNonInOutBase(*this, e, accessKind, options, orig);
   CanType formalType = getSubstFormalRValueType(e);
   auto typeData = getValueTypeData(accessKind, formalType, rv.getValue());
   LValue lv;
   lv.add<ValueComponent>(rv, None, typeData, /*isRValue=*/true);
   return lv;
}

LValue PILGenLValue::visitExpr(Expr *e, SGFAccessKind accessKind,
                               LValueOptions options) {
   e->dump(llvm::errs());
   llvm::errs() << "\n";
   llvm_unreachable("unimplemented lvalue expr");
}

namespace {
/// A CRTP class for emitting accesses.
template <class Impl, class StorageType>
struct AccessEmitter {
   PILGenFunction &SGF;
   StorageType *Storage;
   CanType FormalRValueType;
   SGFAccessKind AccessKind;

   Impl &asImpl() { return static_cast<Impl&>(*this); }

   AccessEmitter(PILGenFunction &SGF, StorageType *storage,
                 SGFAccessKind accessKind, CanType formalRValueType)
      : SGF(SGF), Storage(storage), FormalRValueType(formalRValueType),
        AccessKind(accessKind) {}

   void emitUsingStrategy(AccessStrategy strategy) {
      switch (strategy.getKind()) {
         case AccessStrategy::Storage: {
            auto typeData =
               getPhysicalStorageTypeData(SGF.getTypeExpansionContext(), SGF.SGM,
                                          AccessKind, Storage, FormalRValueType);
            return asImpl().emitUsingStorage(typeData);
         }

         case AccessStrategy::DirectToAccessor:
            return asImpl().emitUsingAccessor(strategy.getAccessor(), true);

         case AccessStrategy::DispatchToAccessor:
            return asImpl().emitUsingAccessor(strategy.getAccessor(), false);

         case AccessStrategy::MaterializeToTemporary: {
            auto typeData = getLogicalStorageTypeData(
               SGF.getTypeExpansionContext(), SGF.SGM, AccessKind, FormalRValueType);
            return asImpl().emitUsingMaterialization(strategy.getReadStrategy(),
                                                     strategy.getWriteStrategy(),
                                                     typeData);
         }
      }
      llvm_unreachable("unknown kind");
   }

   void emitUsingAccessor(AccessorKind accessorKind, bool isDirect) {
      auto accessor =
         SGF.SGM.getAccessorDeclRef(Storage->getOpaqueAccessor(accessorKind));

      switch (accessorKind) {
         case AccessorKind::Get:
         case AccessorKind::Set: {
            auto typeData = getLogicalStorageTypeData(
               SGF.getTypeExpansionContext(), SGF.SGM, AccessKind, FormalRValueType);
            return asImpl().emitUsingGetterSetter(accessor, isDirect, typeData);
         }

         case AccessorKind::Address:
         case AccessorKind::MutableAddress: {
            auto typeData =
               getPhysicalStorageTypeData(SGF.getTypeExpansionContext(), SGF.SGM,
                                          AccessKind, Storage, FormalRValueType);
            return asImpl().emitUsingAddressor(accessor, isDirect, typeData);
         }

         case AccessorKind::Read:
         case AccessorKind::Modify: {
            auto typeData =
               getPhysicalStorageTypeData(SGF.getTypeExpansionContext(), SGF.SGM,
                                          AccessKind, Storage, FormalRValueType);
            return asImpl().emitUsingCoroutineAccessor(accessor, isDirect,
                                                       typeData);
         }

         case AccessorKind::WillSet:
         case AccessorKind::DidSet:
            llvm_unreachable("cannot use accessor directly to perform an access");
      }
      llvm_unreachable("bad kind");
   }
};
}

static LValue emitLValueForNonMemberVarDecl(PILGenFunction &SGF,
                                            PILLocation loc,
                                            ConcreteDeclRef declRef,
                                            CanType formalRValueType,
                                            SGFAccessKind accessKind,
                                            LValueOptions options,
                                            AccessSemantics semantics) {
   LValue lv;

   auto *var = cast<VarDecl>(declRef.getDecl());
   auto subs = declRef.getSubstitutions();
   if (!subs)
      subs = SGF.F.getForwardingSubstitutionMap();

   auto access = getFormalAccessKind(accessKind);
   auto strategy = var->getAccessStrategy(semantics, access,
                                          SGF.SGM.M.getPolarphpModule(),
                                          SGF.F.getResilienceExpansion());

   lv.addNonMemberVarComponent(SGF, loc, var, subs,
                               options, accessKind, strategy, formalRValueType);

   return lv;
}

void LValue::addNonMemberVarComponent(PILGenFunction &SGF, PILLocation loc,
                                      VarDecl *var,
                                      SubstitutionMap subs,
                                      LValueOptions options,
                                      SGFAccessKind accessKind,
                                      AccessStrategy strategy,
                                      CanType formalRValueType) {
   struct NonMemberVarAccessEmitter :
      AccessEmitter<NonMemberVarAccessEmitter, VarDecl> {
      LValue &LV;
      PILLocation Loc;
      SubstitutionMap Subs;
      LValueOptions Options;

      NonMemberVarAccessEmitter(PILGenFunction &SGF, PILLocation loc,
                                VarDecl *var, SubstitutionMap subs,
                                SGFAccessKind accessKind,
                                CanType formalRValueType,
                                LValueOptions options, LValue &lv)
         : AccessEmitter(SGF, var, accessKind, formalRValueType),
           LV(lv), Loc(loc), Subs(subs), Options(options) {}

      void emitUsingAddressor(PILDeclRef addressor, bool isDirect,
                              LValueTypeData typeData) {
         PILType storageType =
            SGF.getLoweredType(Storage->getType()).getAddressType();
         LV.add<AddressorComponent>(Storage, addressor,
            /*isSuper=*/false, isDirect, Subs,
                                    CanType(), typeData, storageType, nullptr,
                                    PreparedArguments(),
            /* isOnSelfParameter */ false);
      }

      void emitUsingCoroutineAccessor(PILDeclRef accessor, bool isDirect,
                                      LValueTypeData typeData) {
         LV.add<CoroutineAccessorComponent>(
            Storage, accessor,
            /*isSuper*/ false, isDirect, Subs, CanType(), typeData, nullptr,
            PreparedArguments(), /*isOnSelfParameter*/ false);
      }

      void emitUsingGetterSetter(PILDeclRef accessor, bool isDirect,
                                 LValueTypeData typeData) {
         LV.add<GetterSetterComponent>(
            Storage, accessor,
            /*isSuper=*/false, isDirect, Subs, CanType(), typeData, nullptr,
            PreparedArguments(), /* isOnSelfParameter */ false);
      }

      void emitUsingMaterialization(AccessStrategy readStrategy,
                                    AccessStrategy writeStrategy,
                                    LValueTypeData typeData) {
         LV.add<MaterializeToTemporaryComponent>(
            Storage, /*super*/ false, Subs, Options, readStrategy,
            writeStrategy,
            /*base type*/ CanType(), typeData, nullptr, PreparedArguments(),
            /* isOnSelfParameter */ false);
      }

      void emitUsingStorage(LValueTypeData typeData) {
         // If it's a physical value (e.g. a local variable in memory), push its
         // address.

         // Check for a local (possibly captured) variable.
         auto address = SGF.maybeEmitValueOfLocalVarDecl(Storage);

         // The only other case that should get here is a global variable.
         if (!address) {
            address = SGF.emitGlobalVariableRef(Loc, Storage);
         }
         assert(address.isLValue() &&
                "physical lvalue decl ref must evaluate to an address");

         Optional<PILAccessEnforcement> enforcement;
         if (!Storage->isLet()) {
            if (Options.IsNonAccessing) {
               enforcement = None;
            } else if (Storage->getDeclContext()->isLocalContext()) {
               enforcement = SGF.getUnknownEnforcement(Storage);
            } else if (Storage->getDeclContext()->isModuleScopeContext()) {
               enforcement = SGF.getDynamicEnforcement(Storage);
            } else {
               assert(Storage->getDeclContext()->isTypeContext() &&
                      !Storage->isInstanceMember());
               enforcement = SGF.getDynamicEnforcement(Storage);
            }
         }

         LV.add<ValueComponent>(address, enforcement, typeData);

         if (address.getType().is<ReferenceStorageType>())
            LV.add<OwnershipComponent>(typeData);
      }
   } emitter(SGF, loc, var, subs, accessKind, formalRValueType, options, *this);

   emitter.emitUsingStrategy(strategy);
}

ManagedValue
PILGenFunction::maybeEmitValueOfLocalVarDecl(VarDecl *var) {
   // For local decls, use the address we allocated or the value if we have it.
   auto It = VarLocs.find(var);
   if (It != VarLocs.end()) {
      // If this has an address, return it.  By-value let's have no address.
      PILValue ptr = It->second.value;
      if (ptr->getType().isAddress())
         return ManagedValue::forLValue(ptr);

      // Otherwise, it is an RValue let.  Uses of it are borrows, but we don't
      // want to proactively emit a borrow here.
      // TODO: integrate this with how callers want these values so we can do
      // something more semantic than just forUnmanaged.
      return ManagedValue::forUnmanaged(ptr);
   }

   // Otherwise, it's non-local or not stored.
   return ManagedValue();
}

ManagedValue
PILGenFunction::emitAddressOfLocalVarDecl(PILLocation loc, VarDecl *var,
                                          CanType formalRValueType,
                                          SGFAccessKind accessKind) {
   assert(var->getDeclContext()->isLocalContext());
   assert(var->getImplInfo().isSimpleStored());
   auto address = maybeEmitValueOfLocalVarDecl(var);
   assert(address);
   assert(address.isLValue());
   return address;
}

RValue PILGenFunction::emitRValueForNonMemberVarDecl(PILLocation loc,
                                                     ConcreteDeclRef declRef,
                                                     CanType formalRValueType,
                                                     AccessSemantics semantics,
                                                     SGFContext C) {
   // Any writebacks for this access are tightly scoped.
   FormalEvaluationScope scope(*this);

   auto *var = cast<VarDecl>(declRef.getDecl());
   auto localValue = maybeEmitValueOfLocalVarDecl(var);

   // If this VarDecl is represented as an address, emit it as an lvalue, then
   // perform a load to get the rvalue.
   if (localValue && localValue.isLValue()) {
      bool guaranteedValid = false;
      IsTake_t shouldTake = IsNotTake;

      // We should only end up in this path for local and global variables,
      // i.e. ones whose lifetime is assured for the duration of the evaluation.
      // Therefore, if the variable is a constant, the value is guaranteed
      // valid as well.
      if (var->isLet())
         guaranteedValid = true;

      // Protect the lvalue read with access markers. The !is<LValueType> assert
      // above ensures that the "LValue" is actually immutable, so we use an
      // unenforced access marker.
      PILValue destAddr = localValue.getLValueAddress();
      PILValue accessAddr = UnenforcedFormalAccess::enter(*this, loc, destAddr,
                                                          PILAccessKind::Read);
      auto propagateRValuePastAccess = [&](RValue &&rvalue) {
         // Check if a new begin_access was emitted and returned as the
         // RValue. This means that the load did not actually load. If so, then
         // fix the rvalue to begin_access operand. The end_access cleanup
         // doesn't change. FIXME: this can't happen with sil-opaque-values.
         if (accessAddr != destAddr && rvalue.isComplete()
             && rvalue.isPlusZero(*this) && !isa<TupleType>(rvalue.getType())) {
            auto mv = std::move(rvalue).getScalarValue();
            if (mv.getValue() == accessAddr)
               mv = std::move(mv).transform(
                  cast<BeginAccessInst>(accessAddr)->getOperand());
            return RValue(*this, loc, formalRValueType, mv);
         }
         return std::move(rvalue);
      };
      // If we have self, see if we are in an 'init' delegation sequence. If so,
      // call out to the special delegation init routine. Otherwise, use the
      // normal RValue emission logic.
      if (var->getName() == getAstContext().Id_self &&
          SelfInitDelegationState != NormalSelf) {
         auto rvalue =
            emitRValueForSelfInDelegationInit(loc, formalRValueType, accessAddr, C);
         return propagateRValuePastAccess(std::move(rvalue));
      }

      // Avoid computing an abstraction pattern for local variables.
      // This is a slight compile-time optimization, but more importantly
      // it avoids problems where locals don't always have interface types.
      if (var->getDeclContext()->isLocalContext()) {
         auto &rvalueTL = getTypeLowering(formalRValueType);
         auto rvalue = RValue(*this, loc, formalRValueType,
                              emitLoad(loc, accessAddr, rvalueTL,
                                       C, shouldTake, guaranteedValid));

         return propagateRValuePastAccess(std::move(rvalue));
      }

      // Otherwise, do the full thing where we potentially bridge and
      // reabstract the declaration.
      auto origFormalType = SGM.Types.getAbstractionPattern(var);
      auto rvalue = RValue(*this, loc, formalRValueType,
                           emitLoad(loc, accessAddr, origFormalType,
                                    formalRValueType,
                                    getTypeLowering(formalRValueType),
                                    C, shouldTake, guaranteedValid));
      return propagateRValuePastAccess(std::move(rvalue));
   }

   // For local decls, use the address we allocated or the value if we have it.
   if (localValue) {
      // Mutable lvalue and address-only 'let's are LValues.
      assert(!localValue.getType().isAddress() &&
             "LValue cases should be handled above");

      PILValue Scalar = localValue.getUnmanagedValue();

      // For weak and unowned types, convert the reference to the right
      // pointer.
      if (Scalar->getType().is<ReferenceStorageType>()) {
         Scalar = emitConversionToSemanticRValue(loc, Scalar,
                                                 getTypeLowering(formalRValueType));
         // emitConversionToSemanticRValue always produces a +1 strong result.
         return RValue(*this, loc, formalRValueType,
                       emitManagedRValueWithCleanup(Scalar));
      }

      // This is a let, so we can make guarantees, so begin the borrow scope.
      ManagedValue Result = emitManagedBeginBorrow(loc, Scalar);

      // If the client can't handle a +0 result, retain it to get a +1.
      // This is a 'let', so we can make guarantees.
      return RValue(*this, loc, formalRValueType,
                    C.isGuaranteedPlusZeroOk()
                    ? Result : Result.copyUnmanaged(*this, loc));
   }

   LValue lv = emitLValueForNonMemberVarDecl(*this, loc, declRef,
                                             formalRValueType,
                                             SGFAccessKind::OwnedObjectRead,
                                             LValueOptions(), semantics);
   return emitLoadOfLValue(loc, std::move(lv), C);
}

LValue PILGenLValue::visitDiscardAssignmentExpr(DiscardAssignmentExpr *e,
                                                SGFAccessKind accessKind,
                                                LValueOptions options) {
   LValueTypeData typeData = getValueTypeData(SGF, accessKind, e);

   PILValue address = SGF.emitTemporaryAllocation(e,
                                                  PILType::getPrimitiveObjectType(
                                                     typeData.TypeOfRValue));
   address = SGF.B.createMarkUninitialized(e, address,
                                           MarkUninitializedInst::Var);
   LValue lv;
   lv.add<ValueComponent>(SGF.emitManagedBufferWithCleanup(address),
                          None, typeData);
   return lv;
}


LValue PILGenLValue::visitDeclRefExpr(DeclRefExpr *e, SGFAccessKind accessKind,
                                      LValueOptions options) {
   return emitLValueForNonMemberVarDecl(SGF, e, e->getDeclRef(),
                                        getSubstFormalRValueType(e),
                                        accessKind, options,
                                        e->getAccessSemantics());
}

LValue PILGenLValue::visitOpaqueValueExpr(OpaqueValueExpr *e,
                                          SGFAccessKind accessKind,
                                          LValueOptions options) {
   // Handle an opaque lvalue that refers to an opened existential.
   auto known = SGF.OpaqueValueExprs.find(e);
   if (known != SGF.OpaqueValueExprs.end()) {
      // Dig the open-existential expression out of the list.
      OpenExistentialExpr *opened = known->second;
      SGF.OpaqueValueExprs.erase(known);

      // Do formal evaluation of the underlying existential lvalue.
      auto lv = visitRec(opened->getExistentialValue(), accessKind, options);
      lv = SGF.emitOpenExistentialLValue(
         opened, std::move(lv),
         CanArchetypeType(opened->getOpenedArchetype()),
         e->getType()->getWithoutSpecifierType()->getCanonicalType(),
         accessKind);
      return lv;
   }

   assert(SGF.OpaqueValues.count(e) && "Didn't bind OpaqueValueExpr");
   auto value = SGF.OpaqueValues[e];

   RegularLocation loc(e);
   LValue lv;
   lv.add<ValueComponent>(value.formalAccessBorrow(SGF, loc), None,
                          getValueTypeData(SGF, accessKind, e));
   return lv;
}

LValue PILGenLValue::visitDotSyntaxBaseIgnoredExpr(DotSyntaxBaseIgnoredExpr *e,
                                                   SGFAccessKind accessKind,
                                                   LValueOptions options) {
   SGF.emitIgnoredExpr(e->getLHS());
   return visitRec(e->getRHS(), accessKind, options);
}

static SGFAccessKind getBaseAccessKindForAccessor(PILGenModule &SGM,
                                                  AccessorDecl *accessor,
                                                  CanType baseFormalType) {
   if (accessor->isMutating()) {
      return SGFAccessKind::ReadWrite;
   } else if (SGM.shouldEmitSelfAsRValue(accessor, baseFormalType)) {
      return SGM.isNonMutatingSelfIndirect(SGM.getAccessorDeclRef(accessor))
             ? SGFAccessKind::OwnedAddressRead
             : SGFAccessKind::OwnedObjectRead;
   } else {
      return SGM.isNonMutatingSelfIndirect(SGM.getAccessorDeclRef(accessor))
             ? SGFAccessKind::BorrowedAddressRead
             : SGFAccessKind::BorrowedObjectRead;
   }
}

static SGFAccessKind getBaseAccessKindForStorage(SGFAccessKind accessKind) {
   // Assume that the member only partially projects the enclosing value,
   // so a write to the member is a read/write of the base.
   return (accessKind == SGFAccessKind::Write
           ? SGFAccessKind::ReadWrite : accessKind);
}

/// Return the appropriate access kind for the base l-value of a
/// particular member, which is being accessed in a particular way.
static SGFAccessKind getBaseAccessKind(PILGenModule &SGM,
                                       AbstractStorageDecl *member,
                                       SGFAccessKind accessKind,
                                       AccessStrategy strategy,
                                       CanType baseFormalType) {
   switch (strategy.getKind()) {
      case AccessStrategy::Storage:
         return getBaseAccessKindForStorage(accessKind);

      case AccessStrategy::MaterializeToTemporary: {
         assert(accessKind == SGFAccessKind::ReadWrite);
         auto writeBaseKind = getBaseAccessKind(SGM, member, SGFAccessKind::Write,
                                                strategy.getWriteStrategy(),
                                                baseFormalType);

         // Fast path for the common case that the write will need to mutate
         // the base.
         if (writeBaseKind == SGFAccessKind::ReadWrite)
            return writeBaseKind;

         auto readBaseKind = getBaseAccessKind(SGM, member,
                                               SGFAccessKind::OwnedAddressRead,
                                               strategy.getReadStrategy(),
                                               baseFormalType);

         // If they're the same kind, just use that.
         if (readBaseKind == writeBaseKind)
            return readBaseKind;

         // If either access is mutating, the total access is a read-write.
         if (!isReadAccess(readBaseKind) || !isReadAccess(writeBaseKind))
            return SGFAccessKind::ReadWrite;

         // Okay, we have two different kinds of read somehow for different
         // accessors of the same storage?
         return SGFAccessKind::OwnedObjectRead;
      }

      case AccessStrategy::DirectToAccessor:
      case AccessStrategy::DispatchToAccessor: {
         auto accessor = member->getOpaqueAccessor(strategy.getAccessor());
         return getBaseAccessKindForAccessor(SGM, accessor, baseFormalType);
      }

   }
   llvm_unreachable("bad access strategy");
}

static CanType getBaseFormalType(Expr *baseExpr) {
   return baseExpr->getType()->getWithoutSpecifierType()->getCanonicalType();
}

bool isCallToReplacedInDynamicReplacement(PILGenFunction &SGF,
                                          AbstractFunctionDecl *afd,
                                          bool &isObjCReplacementSelfCall);

static bool isCallToSelfOfCurrentFunction(PILGenFunction &SGF, LookupExpr *e) {
   return SGF.FunctionDC->getAsDecl() &&
          isa<AbstractFunctionDecl>(SGF.FunctionDC->getAsDecl()) &&
          e->getBase()->isSelfExprOf(
             cast<AbstractFunctionDecl>(SGF.FunctionDC->getAsDecl()), false);
}

static bool isCurrentFunctionReadAccess(PILGenFunction &SGF) {
   auto *contextAccessorDecl =
      dyn_cast_or_null<AccessorDecl>(SGF.FunctionDC->getAsDecl());
   return contextAccessorDecl &&
          contextAccessorDecl->getAccessorKind() == AccessorKind::Read;
}

LValue PILGenLValue::visitMemberRefExpr(MemberRefExpr *e,
                                        SGFAccessKind accessKind,
                                        LValueOptions options) {
   // MemberRefExpr can refer to type and function members, but the only case
   // that can be an lvalue is a VarDecl.
   VarDecl *var = cast<VarDecl>(e->getMember().getDecl());

   auto accessSemantics = e->getAccessSemantics();
   AccessStrategy strategy =
      var->getAccessStrategy(accessSemantics,
                             getFormalAccessKind(accessKind),
                             SGF.SGM.M.getPolarphpModule(),
                             SGF.F.getResilienceExpansion());

   bool isOnSelfParameter = isCallToSelfOfCurrentFunction(SGF, e);

   bool isContextRead = isCurrentFunctionReadAccess(SGF);

   // If we are inside _read, calling self.get, and the _read we are inside of is
   // the same as the as self's variable and the current function is a
   // dynamic replacement directly call the implementation.
   if (isContextRead && isOnSelfParameter && strategy.hasAccessor() &&
       strategy.getAccessor() == AccessorKind::Get &&
       var->getOpaqueAccessor(AccessorKind::Read)) {
      bool isObjC = false;
      auto readAccessor =
         SGF.SGM.getAccessorDeclRef(var->getOpaqueAccessor(AccessorKind::Read));
      if (isCallToReplacedInDynamicReplacement(
         SGF, readAccessor.getAbstractFunctionDecl(), isObjC)) {
         accessSemantics = AccessSemantics::DirectToImplementation;
         strategy = var->getAccessStrategy(
            accessSemantics,
            getFormalAccessKind(accessKind),
            SGF.SGM.M.getPolarphpModule(),
            SGF.F.getResilienceExpansion());
      }
   }

   LValue lv = visitRec(e->getBase(),
                        getBaseAccessKind(SGF.SGM, var, accessKind, strategy,
                                          getBaseFormalType(e->getBase())),
                        getBaseOptions(options, strategy));
   assert(lv.isValid());

   CanType substFormalRValueType = getSubstFormalRValueType(e);
   lv.addMemberVarComponent(SGF, e, var, e->getMember().getSubstitutions(),
                            options, e->isSuper(), accessKind, strategy,
                            substFormalRValueType, isOnSelfParameter);
   return lv;
}

namespace {

/// A CRTP class for emitting member accesses.
template <class Impl, class StorageType>
struct MemberStorageAccessEmitter : AccessEmitter<Impl, StorageType> {
   using super = AccessEmitter<Impl, StorageType>;
   using super::SGF;
   using super::Storage;
   using super::FormalRValueType;
   LValue &LV;
   LValueOptions Options;
   PILLocation Loc;
   bool IsSuper;
   bool IsOnSelfParameter; // Is self the self parameter in context.
   CanType BaseFormalType;
   SubstitutionMap Subs;
   Expr *IndexExprForDiagnostics;
   PreparedArguments Indices;

   MemberStorageAccessEmitter(PILGenFunction &SGF, PILLocation loc,
                              StorageType *storage, SubstitutionMap subs,
                              bool isSuper, SGFAccessKind accessKind,
                              CanType formalRValueType, LValueOptions options,
                              LValue &lv, Expr *indexExprForDiagnostics,
                              PreparedArguments &&indices, bool isSelf = false)
      : super(SGF, storage, accessKind, formalRValueType), LV(lv),
        Options(options), Loc(loc), IsSuper(isSuper), IsOnSelfParameter(isSelf),
        BaseFormalType(lv.getSubstFormalType()), Subs(subs),
        IndexExprForDiagnostics(indexExprForDiagnostics),
        Indices(std::move(indices)) {}

   void emitUsingAddressor(PILDeclRef addressor, bool isDirect,
                           LValueTypeData typeData) {
      PILType varStorageType = SGF.SGM.Types.getSubstitutedStorageType(
         SGF.getTypeExpansionContext(), Storage, FormalRValueType);

      LV.add<AddressorComponent>(Storage, addressor, IsSuper, isDirect, Subs,
                                 BaseFormalType, typeData, varStorageType,
                                 IndexExprForDiagnostics, std::move(Indices),
                                 IsOnSelfParameter);
   }

   void emitUsingCoroutineAccessor(PILDeclRef accessor, bool isDirect,
                                   LValueTypeData typeData) {
      LV.add<CoroutineAccessorComponent>(
         Storage, accessor, IsSuper, isDirect, Subs, BaseFormalType, typeData,
         IndexExprForDiagnostics, std::move(Indices), IsOnSelfParameter);
   }

   void emitUsingGetterSetter(PILDeclRef accessor, bool isDirect,
                              LValueTypeData typeData) {
      LV.add<GetterSetterComponent>(
         Storage, accessor, IsSuper, isDirect, Subs, BaseFormalType, typeData,
         IndexExprForDiagnostics, std::move(Indices), IsOnSelfParameter);
   }

   void emitUsingMaterialization(AccessStrategy readStrategy,
                                 AccessStrategy writeStrategy,
                                 LValueTypeData typeData) {
      LV.add<MaterializeToTemporaryComponent>(
         Storage, IsSuper, Subs, Options, readStrategy, writeStrategy,
         BaseFormalType, typeData, IndexExprForDiagnostics, std::move(Indices),
         IsOnSelfParameter);
   }
};
} // end anonymous namespace

void LValue::addMemberVarComponent(PILGenFunction &SGF, PILLocation loc,
                                   VarDecl *var,
                                   SubstitutionMap subs,
                                   LValueOptions options,
                                   bool isSuper,
                                   SGFAccessKind accessKind,
                                   AccessStrategy strategy,
                                   CanType formalRValueType,
                                   bool isOnSelfParameter) {
   struct MemberVarAccessEmitter
      : MemberStorageAccessEmitter<MemberVarAccessEmitter, VarDecl> {
      using MemberStorageAccessEmitter::MemberStorageAccessEmitter;

      void emitUsingStorage(LValueTypeData typeData) {
         // For static variables, emit a reference to the global variable backing
         // them.
         // FIXME: This has to be dynamically looked up for classes, and
         // dynamically instantiated for generics.
         if (Storage->isStatic()) {
            // FIXME: this implicitly drops the earlier components, but maybe
            // we ought to evaluate them for side-effects even during the
            // formal access?
            LV.Path.clear();
            LV.addNonMemberVarComponent(SGF, Loc, Storage, Subs, Options,
                                        typeData.getAccessKind(),
                                        AccessStrategy::getStorage(),
                                        FormalRValueType);
            return;
         }

         // Otherwise, it's a physical member.
         PILType varStorageType = SGF.SGM.Types.getSubstitutedStorageType(
            SGF.getTypeExpansionContext(), Storage, FormalRValueType);

         if (BaseFormalType->mayHaveSuperclass()) {
            LV.add<RefElementComponent>(Storage, Options, varStorageType, typeData);
         } else {
            assert(BaseFormalType->getStructOrBoundGenericStruct());
            LV.add<StructElementComponent>(Storage, varStorageType, typeData);
         }

         // If the member has weak or unowned storage, convert it away.
         if (varStorageType.is<ReferenceStorageType>()) {
            LV.add<OwnershipComponent>(typeData);
         }
      }
   } emitter(SGF, loc, var, subs, isSuper, accessKind,
             formalRValueType, options, *this,
      /*indices for diags*/ nullptr, /*indices*/ PreparedArguments(),
             isOnSelfParameter);

   emitter.emitUsingStrategy(strategy);
}

LValue PILGenLValue::visitSubscriptExpr(SubscriptExpr *e,
                                        SGFAccessKind accessKind,
                                        LValueOptions options) {
   auto decl = cast<SubscriptDecl>(e->getDecl().getDecl());
   auto subs = e->getDecl().getSubstitutions();


   auto accessSemantics = e->getAccessSemantics();
   auto strategy =
      decl->getAccessStrategy(accessSemantics,
                              getFormalAccessKind(accessKind),
                              SGF.SGM.M.getPolarphpModule(),
                              SGF.F.getResilienceExpansion());

   bool isOnSelfParameter = isCallToSelfOfCurrentFunction(SGF, e);
   bool isContextRead = isCurrentFunctionReadAccess(SGF);

   // If we are inside _read, calling self.get, and the _read we are inside of is
   // the same as the as self's variable and the current function is a
   // dynamic replacement directly call the implementation.
   if (isContextRead && isOnSelfParameter && strategy.hasAccessor() &&
       strategy.getAccessor() == AccessorKind::Get &&
       decl->getOpaqueAccessor(AccessorKind::Read)) {
      bool isObjC = false;
      auto readAccessor =
         SGF.SGM.getAccessorDeclRef(decl->getOpaqueAccessor(AccessorKind::Read));
      if (isCallToReplacedInDynamicReplacement(
         SGF, readAccessor.getAbstractFunctionDecl(), isObjC)) {
         accessSemantics = AccessSemantics::DirectToImplementation;
         strategy = decl->getAccessStrategy(
            accessSemantics,
            getFormalAccessKind(accessKind),
            SGF.SGM.M.getPolarphpModule(),
            SGF.F.getResilienceExpansion());
      }
   }

   LValue lv = visitRec(e->getBase(),
                        getBaseAccessKind(SGF.SGM, decl, accessKind, strategy,
                                          getBaseFormalType(e->getBase())),
                        getBaseOptions(options, strategy));
   assert(lv.isValid());

   Expr *indexExpr = e->getIndex();
   auto indices = SGF.prepareSubscriptIndices(decl, subs, strategy, indexExpr);

   CanType formalRValueType = getSubstFormalRValueType(e);
   lv.addMemberSubscriptComponent(SGF, e, decl, subs,
                                  options, e->isSuper(), accessKind, strategy,
                                  formalRValueType, std::move(indices),
                                  indexExpr, isOnSelfParameter);
   return lv;
}

LValue PILGenLValue::visitKeyPathApplicationExpr(KeyPathApplicationExpr *e,
                                                 SGFAccessKind accessKind,
                                                 LValueOptions options) {
   auto keyPathExpr = e->getKeyPath();
   auto keyPathKind =
      *keyPathExpr->getType()->getAnyNominal()->getKeyPathTypeKind();

   // Determine the base access strategy based on the strategy of this access.
   SGFAccessKind subAccess;
   if (!isReadAccess(accessKind)) {
      // The only read-write accesses we allow are with WritableKeyPath and
      // ReferenceWritableKeyPath.
      assert(keyPathKind == KPTK_WritableKeyPath ||
             keyPathKind == KPTK_ReferenceWritableKeyPath);
      subAccess = (keyPathKind == KPTK_ReferenceWritableKeyPath
                   ? SGFAccessKind::BorrowedAddressRead
                   : SGFAccessKind::ReadWrite);
   } else {
      // For all the other kinds, we want the emit the base as an address
      // r-value; we don't support key paths for storage with mutating read
      // operations.
      subAccess = SGFAccessKind::BorrowedAddressRead;
   }

   // For now, just ignore any options we were given.
   LValueOptions subOptions = LValueOptions();

   // Do formal evaluation of the base l-value.
   // The base should be reabstracted to the maximal abstraction pattern.
   LValue lv = visitRec(e->getBase(), subAccess, subOptions,
                        AbstractionPattern::getOpaque());

   // Emit the key path r-value.
   auto keyPath = SGF.emitRValueAsSingleValue(keyPathExpr);

   // The result will end up projected at the maximal abstraction level too.
   auto substFormalType = e->getType()->getRValueType()->getCanonicalType();

   bool useLogical = [&] {
      switch (accessKind) {
         // Use the physical 'read' pattern for these unless we're working with
         // AnyKeyPath or PartialKeyPath, which only have getters.
         case SGFAccessKind::BorrowedAddressRead:
         case SGFAccessKind::BorrowedObjectRead:
            return (keyPathKind == KPTK_AnyKeyPath ||
                    keyPathKind == KPTK_PartialKeyPath);

         case SGFAccessKind::OwnedObjectRead:
         case SGFAccessKind::OwnedAddressRead:
         case SGFAccessKind::IgnoredRead: // should we use physical for this?
         case SGFAccessKind::Write:
            return true;

         case SGFAccessKind::ReadWrite:
            return false;
      }
      llvm_unreachable("bad access kind");
   }();

   if (useLogical) {
      auto typeData = getLogicalStorageTypeData(
         SGF.getTypeExpansionContext(), SGF.SGM, accessKind, substFormalType);

      Type baseFormalType = e->getBase()->getType()->getRValueType();
      lv.add<LogicalKeyPathApplicationComponent>(typeData, keyPathKind, keyPath,
                                                 baseFormalType);

      // TODO: make LogicalKeyPathApplicationComponent expect/produce values
      // in the opaque AbstractionPattern and push an OrigToSubstComponent here
      // so it can be peepholed.
   } else {
      auto typeData = getAbstractedTypeData(
         TypeExpansionContext::minimal(), SGF.SGM, accessKind,
         AbstractionPattern::getOpaque(), substFormalType);

      lv.add<PhysicalKeyPathApplicationComponent>(typeData, keyPathKind, keyPath);

      // Reabstract to the substituted abstraction level if necessary.
      auto substResultPILTy = SGF.getLoweredType(substFormalType);
      if (typeData.TypeOfRValue != substResultPILTy.getAstType()) {
         lv.addOrigToSubstComponent(substResultPILTy);
      }
   }

   return lv;
}

void LValue::addMemberSubscriptComponent(PILGenFunction &SGF, PILLocation loc,
                                         SubscriptDecl *decl,
                                         SubstitutionMap subs,
                                         LValueOptions options,
                                         bool isSuper,
                                         SGFAccessKind accessKind,
                                         AccessStrategy strategy,
                                         CanType formalRValueType,
                                         PreparedArguments &&indices,
                                         Expr *indexExprForDiagnostics,
                                         bool isOnSelfParameter) {
   struct MemberSubscriptAccessEmitter
      : MemberStorageAccessEmitter<MemberSubscriptAccessEmitter,
         SubscriptDecl> {
      using MemberStorageAccessEmitter::MemberStorageAccessEmitter;

      void emitUsingStorage(LValueTypeData typeData) {
         llvm_unreachable("subscripts never have storage");
      }
   } emitter(SGF, loc, decl, subs, isSuper, accessKind, formalRValueType,
             options, *this, indexExprForDiagnostics, std::move(indices),
             isOnSelfParameter);

   emitter.emitUsingStrategy(strategy);
}

bool LValue::isObviouslyNonConflicting(const LValue &other,
                                       SGFAccessKind selfAccess,
                                       SGFAccessKind otherAccess) {
   // Reads never conflict with reads.
   if (isReadAccess(selfAccess) && isReadAccess(otherAccess))
      return true;

   // We can cover more cases here.
   return false;
}

LValue PILGenLValue::visitTupleElementExpr(TupleElementExpr *e,
                                           SGFAccessKind accessKind,
                                           LValueOptions options) {
   unsigned index = e->getFieldNumber();
   LValue lv = visitRec(e->getBase(),
                        getBaseAccessKindForStorage(accessKind),
                        options.forProjectedBaseLValue());

   auto baseTypeData = lv.getTypeData();
   LValueTypeData typeData = {
      accessKind,
      baseTypeData.OrigFormalType.getTupleElementType(index),
      cast<TupleType>(baseTypeData.SubstFormalType).getElementType(index),
      cast<TupleType>(baseTypeData.TypeOfRValue).getElementType(index)
   };

   lv.add<TupleElementComponent>(index, typeData);
   return lv;
}

LValue PILGenLValue::visitOpenExistentialExpr(OpenExistentialExpr *e,
                                              SGFAccessKind accessKind,
                                              LValueOptions options) {
   // If the opaque value is not an lvalue, open the existential immediately.
   if (!e->getOpaqueValue()->getType()->is<LValueType>()) {
      return SGF.emitOpenExistentialExpr<LValue>(e,
                                                 [&](Expr *subExpr) -> LValue {
                                                    return visitRec(subExpr,
                                                                    accessKind,
                                                                    options);
                                                 });
   }

   // Record the fact that we're opening this existential. The actual
   // opening operation will occur when we see the OpaqueValueExpr.
   bool inserted = SGF.OpaqueValueExprs.insert({e->getOpaqueValue(), e}).second;
   (void)inserted;
   assert(inserted && "already have this opened existential?");

   // Visit the subexpression.
   LValue lv = visitRec(e->getSubExpr(), accessKind, options);

   // Sanity check that we did see the OpaqueValueExpr.
   assert(SGF.OpaqueValueExprs.count(e->getOpaqueValue()) == 0 &&
          "opened existential not removed?");
   return lv;
}

static LValueTypeData
getOptionalObjectTypeData(PILGenFunction &SGF, SGFAccessKind accessKind,
                          const LValueTypeData &baseTypeData) {
   return {
      accessKind,
      baseTypeData.OrigFormalType.getOptionalObjectType(),
      baseTypeData.SubstFormalType.getOptionalObjectType(),
      baseTypeData.TypeOfRValue.getOptionalObjectType(),
   };
}

LValue PILGenLValue::visitForceValueExpr(ForceValueExpr *e,
                                         SGFAccessKind accessKind,
                                         LValueOptions options) {
   // Like BindOptional, this is a read even if we only write to the result.
   // (But it's unnecessary to use a force this way!)
   LValue lv = visitRec(e->getSubExpr(),
                        getBaseAccessKindForStorage(accessKind),
                        options.forComputedBaseLValue());
   LValueTypeData typeData =
      getOptionalObjectTypeData(SGF, accessKind, lv.getTypeData());
   bool isImplicitUnwrap = e->isImplicit() &&
                           e->isForceOfImplicitlyUnwrappedOptional();
   lv.add<ForceOptionalObjectComponent>(typeData, isImplicitUnwrap);
   return lv;
}

LValue PILGenLValue::visitBindOptionalExpr(BindOptionalExpr *e,
                                           SGFAccessKind accessKind,
                                           LValueOptions options) {
   // Binding reads the base even if we then only write to the result.
   auto baseAccessKind = getBaseAccessKindForStorage(accessKind);

   // We're going to take the address of the base.
   // TODO: deal more efficiently with an object-preferring access.
   baseAccessKind = getAddressAccessKind(baseAccessKind);

   // Do formal evaluation of the base l-value.
   LValue optLV = visitRec(e->getSubExpr(), baseAccessKind,
                           options.forComputedBaseLValue());

   LValueTypeData optTypeData = optLV.getTypeData();
   LValueTypeData valueTypeData =
      getOptionalObjectTypeData(SGF, accessKind, optTypeData);

   // The chaining operator immediately begins a formal access to the
   // base l-value.  In concrete terms, this means we can immediately
   // evaluate the base down to an address.
   ManagedValue optAddr = SGF.emitAddressOfLValue(e, std::move(optLV));

   // Bind the value, branching to the destination address if there's no
   // value there.
   SGF.emitBindOptionalAddress(e, optAddr, e->getDepth());

   // Project out the payload on the success branch.  We can just use a
   // naked ValueComponent here; this is effectively a separate l-value.
   ManagedValue valueAddr =
      getAddressOfOptionalValue(SGF, e, optAddr, valueTypeData);
   LValue valueLV;
   valueLV.add<ValueComponent>(valueAddr, None, valueTypeData);
   return valueLV;
}

LValue PILGenLValue::visitInOutExpr(InOutExpr *e, SGFAccessKind accessKind,
                                    LValueOptions options) {
   return visitRec(e->getSubExpr(), accessKind, options);
}

/// Emit an lvalue that refers to the given property.  This is
/// designed to work with ManagedValue 'base's that are either +0 or +1.
LValue PILGenFunction::emitPropertyLValue(PILLocation loc, ManagedValue base,
                                          CanType baseFormalType,
                                          VarDecl *ivar,
                                          LValueOptions options,
                                          SGFAccessKind accessKind,
                                          AccessSemantics semantics) {
   PILGenLValue sgl(*this);
   LValue lv;

   auto baseType = base.getType().getAstType();
   auto subMap = baseType->getContextSubstitutionMap(
      SGM.M.getPolarphpModule(), ivar->getDeclContext());

   AccessStrategy strategy =
      ivar->getAccessStrategy(semantics,
                              getFormalAccessKind(accessKind),
                              SGM.M.getPolarphpModule(),
                              F.getResilienceExpansion());

   auto baseAccessKind =
      getBaseAccessKind(SGM, ivar, accessKind, strategy, baseFormalType);

   LValueTypeData baseTypeData =
      getValueTypeData(baseAccessKind, baseFormalType, base.getValue());

   // Refer to 'self' as the base of the lvalue.
   lv.add<ValueComponent>(base, None, baseTypeData,
      /*isRValue=*/!base.isLValue());

   auto substFormalType = ivar->getValueInterfaceType().subst(subMap)
      ->getCanonicalType();

   lv.addMemberVarComponent(*this, loc, ivar, subMap, options, /*super*/ false,
                            accessKind, strategy, substFormalType);
   return lv;
}

// This is emitLoad that will handle re-abstraction and bridging for the client.
ManagedValue PILGenFunction::emitLoad(PILLocation loc, PILValue addr,
                                      AbstractionPattern origFormalType,
                                      CanType substFormalType,
                                      const TypeLowering &rvalueTL,
                                      SGFContext C, IsTake_t isTake,
                                      bool isAddressGuaranteed) {
   assert(addr->getType().isAddress());
   PILType addrRValueType = addr->getType().getReferenceStorageReferentType();

   // Fast path: the types match exactly.
   if (addrRValueType == rvalueTL.getLoweredType().getAddressType()) {
      return emitLoad(loc, addr, rvalueTL, C, isTake, isAddressGuaranteed);
   }

   // Otherwise, we need to reabstract or bridge.
   auto conversion =
      origFormalType.isClangType()
      ? Conversion::getBridging(Conversion::BridgeFromObjC,
                                origFormalType.getType(),
                                substFormalType, rvalueTL.getLoweredType())
      : Conversion::getOrigToSubst(origFormalType, substFormalType);

   return emitConvertedRValue(loc, conversion, C,
                              [&](PILGenFunction &SGF, PILLocation loc, SGFContext C) {
                                 return SGF.emitLoad(loc, addr, getTypeLowering(addrRValueType),
                                                     C, isTake, isAddressGuaranteed);
                              });
}

/// Load an r-value out of the given address.
///
/// \param rvalueTL - the type lowering for the type-of-rvalue
///   of the address
/// \param isAddrGuaranteed - true if the value in this address
///   is guaranteed to be valid for the duration of the current
///   evaluation (see SGFContext::AllowGuaranteedPlusZero)
ManagedValue PILGenFunction::emitLoad(PILLocation loc, PILValue addr,
                                      const TypeLowering &rvalueTL,
                                      SGFContext C, IsTake_t isTake,
                                      bool isAddrGuaranteed) {
   // Get the lowering for the address type.  We can avoid a re-lookup
   // in the very common case of this being equivalent to the r-value
   // type.
   auto &addrTL =
      (addr->getType() == rvalueTL.getLoweredType().getAddressType()
       ? rvalueTL : getTypeLowering(addr->getType()));

   // Never do a +0 load together with a take.
   bool isPlusZeroOk = (isTake == IsNotTake &&
                        (isAddrGuaranteed ? C.isGuaranteedPlusZeroOk()
                                          : C.isImmediatePlusZeroOk()));

   if (rvalueTL.isAddressOnly() && silConv.useLoweredAddresses()) {
      // If the client is cool with a +0 rvalue, the decl has an address-only
      // type, and there are no conversions, then we can return this as a +0
      // address RValue.
      if (isPlusZeroOk && rvalueTL.getLoweredType() == addrTL.getLoweredType())
         return ManagedValue::forUnmanaged(addr);

      // Copy the address-only value.
      return B.bufferForExpr(
         loc, rvalueTL.getLoweredType(), rvalueTL, C,
         [&](PILValue newAddr) {
            emitSemanticLoadInto(loc, addr, addrTL, newAddr, rvalueTL,
                                 isTake, IsInitialization);
         });
   }

   // Ok, this is something loadable.  If this is a non-take access at plus zero,
   // we can perform a +0 load of the address instead of materializing a +1
   // value.
   if (isPlusZeroOk && addrTL.getLoweredType() == rvalueTL.getLoweredType()) {
      return B.createLoadBorrow(loc, ManagedValue::forUnmanaged(addr));
   }

   // Load the loadable value, and retain it if we aren't taking it.
   PILValue loadedV = emitSemanticLoad(loc, addr, addrTL, rvalueTL, isTake);
   return emitManagedRValueWithCleanup(loadedV);
}

/// Load an r-value out of the given address.
///
/// \param rvalueTL - the type lowering for the type-of-rvalue
///   of the address
/// \param isAddressGuaranteed - true if the value in this address
///   is guaranteed to be valid for the duration of the current
///   evaluation (see SGFContext::AllowGuaranteedPlusZero)
ManagedValue PILGenFunction::emitFormalAccessLoad(PILLocation loc,
                                                  PILValue addr,
                                                  const TypeLowering &rvalueTL,
                                                  SGFContext C, IsTake_t isTake,
                                                  bool isAddressGuaranteed) {
   // Get the lowering for the address type.  We can avoid a re-lookup
   // in the very common case of this being equivalent to the r-value
   // type.
   auto &addrTL = (addr->getType() == rvalueTL.getLoweredType().getAddressType()
                   ? rvalueTL
                   : getTypeLowering(addr->getType()));

   // Never do a +0 load together with a take.
   bool isPlusZeroOk =
      (isTake == IsNotTake && (isAddressGuaranteed ? C.isGuaranteedPlusZeroOk()
                                                   : C.isImmediatePlusZeroOk()));

   if (rvalueTL.isAddressOnly() && silConv.useLoweredAddresses()) {
      // If the client is cool with a +0 rvalue, the decl has an address-only
      // type, and there are no conversions, then we can return this as a +0
      // address RValue.
      if (isPlusZeroOk && rvalueTL.getLoweredType() == addrTL.getLoweredType())
         return ManagedValue::forUnmanaged(addr);

      // Copy the address-only value.
      return B.formalAccessBufferForExpr(
         loc, rvalueTL.getLoweredType(), rvalueTL, C,
         [&](PILValue addressForCopy) {
            emitSemanticLoadInto(loc, addr, addrTL, addressForCopy, rvalueTL,
                                 isTake, IsInitialization);
         });
   }

   // Ok, this is something loadable.  If this is a non-take access at plus zero,
   // we can perform a +0 load of the address instead of materializing a +1
   // value.
   if (isPlusZeroOk && addrTL.getLoweredType() == rvalueTL.getLoweredType()) {
      return B.createFormalAccessLoadBorrow(loc,
                                            ManagedValue::forUnmanaged(addr));
   }

   // Load the loadable value, and retain it if we aren't taking it.
   PILValue loadedV = emitSemanticLoad(loc, addr, addrTL, rvalueTL, isTake);
   return emitFormalAccessManagedRValueWithCleanup(loc, loadedV);
}

static void emitUnloweredStoreOfCopy(PILGenBuilder &B, PILLocation loc,
                                     PILValue value, PILValue addr,
                                     IsInitialization_t isInit) {
   if (isInit) {
      B.emitStoreValueOperation(loc, value, addr, StoreOwnershipQualifier::Init);
   } else {
      B.createAssign(loc, value, addr, AssignOwnershipQualifier::Unknown);
   }
}

PILValue PILGenFunction::emitConversionToSemanticRValue(PILLocation loc,
                                                        PILValue src,
                                                        const TypeLowering &valueTL) {
   auto storageType = src->getType();
   auto swiftStorageType = storageType.castTo<ReferenceStorageType>();

   switch (swiftStorageType->getOwnership()) {
      case ReferenceOwnership::Strong:
         llvm_unreachable("strong reference storage type should be impossible");
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
  case ReferenceOwnership::Name: \
    /* Address-only storage types are handled with their underlying type. */ \
    llvm_unreachable("address-only pointers are handled elsewhere");
#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...)                         \
  case ReferenceOwnership::Name:                                               \
    return B.createStrongCopy##Name##Value(loc, src);
#define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...)                      \
  case ReferenceOwnership::Name: {                                             \
    /* For loadable reference storage types, we need to generate a strong */   \
    /* retain and strip the box. */                                            \
    assert(storageType.castTo<Name##StorageType>()->isLoadable(                \
        ResilienceExpansion::Maximal));                                        \
    return B.createStrongCopy##Name##Value(loc, src);                          \
  }
#define UNCHECKED_REF_STORAGE(Name, ...)                                       \
  case ReferenceOwnership::Name: {                                             \
    /* For static reference storage types, we need to strip the box and */     \
    /* then do an (unsafe) retain. */                                          \
    return B.createStrongCopy##Name##Value(loc, src);                          \
  }
#include "polarphp/ast/ReferenceStorageDef.h"
   }
   llvm_unreachable("impossible");
}

ManagedValue PILGenFunction::emitConversionToSemanticRValue(
   PILLocation loc, ManagedValue src, const TypeLowering &valueTL) {
   auto swiftStorageType = src.getType().castTo<ReferenceStorageType>();

   switch (swiftStorageType->getOwnership()) {
      case ReferenceOwnership::Strong:
         llvm_unreachable("strong reference storage type should be impossible");
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
  case ReferenceOwnership::Name: \
    /* Address-only storage types are handled with their underlying type. */ \
    llvm_unreachable("address-only pointers are handled elsewhere");
#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...)            \
  case ReferenceOwnership::Name:                                               \
    /* Generate a strong retain and strip the box. */                          \
    return B.createStrongCopy##Name##Value(loc, src);
#define UNCHECKED_REF_STORAGE(Name, ...)                                       \
  case ReferenceOwnership::Name:                                               \
    /* Strip the box and then do an (unsafe) retain. */                        \
    return B.createStrongCopy##Name##Value(loc, src);
#include "polarphp/ast/ReferenceStorageDef.h"
   }
   llvm_unreachable("impossible");
}

/// Given that the type-of-rvalue differs from the type-of-storage,
/// and given that the type-of-rvalue is loadable, produce a +1 scalar
/// of the type-of-rvalue.
static PILValue emitLoadOfSemanticRValue(PILGenFunction &SGF,
                                         PILLocation loc,
                                         PILValue src,
                                         const TypeLowering &valueTL,
                                         IsTake_t isTake) {
   auto storageType = src->getType();
   auto swiftStorageType = storageType.castTo<ReferenceStorageType>();

   switch (swiftStorageType->getOwnership()) {
      case ReferenceOwnership::Strong:
         llvm_unreachable("strong reference storage type should be impossible");
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
  case ReferenceOwnership::Name: \
    return SGF.B.createLoad##Name(loc, src, isTake);
#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name)          \
  {                                                                            \
    /* For loadable types, we need to strip the box. */                        \
    /* If we are not performing a take, use a load_borrow. */                  \
    if (!isTake) {                                                             \
      PILValue value = SGF.B.createLoadBorrow(loc, src);                       \
      PILValue strongValue = SGF.B.createStrongCopy##Name##Value(loc, value);  \
      SGF.B.createEndBorrow(loc, value, src);                                  \
      return strongValue;                                                      \
    }                                                                          \
    /* Otherwise perform a load take and destroy the stored value. */          \
    auto value =                                                               \
        SGF.B.emitLoadValueOperation(loc, src, LoadOwnershipQualifier::Take);  \
    PILValue strongValue = SGF.B.createStrongCopy##Name##Value(loc, value);    \
    SGF.B.createDestroyValue(loc, value);                                      \
    return strongValue;                                                        \
  }
#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
  case ReferenceOwnership::Name: \
    ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name)
#define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
  case ReferenceOwnership::Name: { \
    /* For loadable types, we need to strip the box. */ \
    auto type = storageType.castTo<Name##StorageType>(); \
    if (!type->isLoadable(ResilienceExpansion::Maximal)) { \
      return SGF.B.createLoad##Name(loc, src, isTake); \
    } \
    ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name) \
  }
#define UNCHECKED_REF_STORAGE(Name, ...)                                       \
  case ReferenceOwnership::Name: {                                             \
    /* For static reference storage types, we need to strip the box. */        \
    auto value = SGF.B.createLoad(loc, src, LoadOwnershipQualifier::Trivial);  \
    return SGF.B.createStrongCopy##Name##Value(loc, value);                    \
  }
#include "polarphp/ast/ReferenceStorageDef.h"
#undef ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER
   }
   llvm_unreachable("unhandled ownership");
}

/// Given that the type-of-rvalue differs from the type-of-storage,
/// store a +1 value (possibly not a scalar) of the type-of-rvalue
/// into the given address.
static void emitStoreOfSemanticRValue(PILGenFunction &SGF,
                                      PILLocation loc,
                                      PILValue value,
                                      PILValue dest,
                                      const TypeLowering &valueTL,
                                      IsInitialization_t isInit) {
   auto storageType = dest->getType();
   auto swiftStorageType = storageType.castTo<ReferenceStorageType>();

   switch (swiftStorageType->getOwnership()) {
      case ReferenceOwnership::Strong:
         llvm_unreachable("strong reference storage type should be impossible");
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
  case ReferenceOwnership::Name: { \
    SGF.B.createStore##Name(loc, value, dest, isInit); \
    /* store doesn't take ownership of the input, so cancel it out. */ \
    SGF.B.emitDestroyValueOperation(loc, value); \
    return; \
  }
#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name) \
  { \
    auto typedValue = SGF.B.createRefTo##Name(loc, value, \
                                              storageType.getObjectType()); \
    auto copiedVal = SGF.B.createCopyValue(loc, typedValue); \
    emitUnloweredStoreOfCopy(SGF.B, loc, copiedVal, dest, isInit); \
    SGF.B.emitDestroyValueOperation(loc, value); \
    return; \
  }
#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
  case ReferenceOwnership::Name: \
    ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name)
#define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
  case ReferenceOwnership::Name: { \
    /* For loadable types, we need to enter the box by */ \
    /* turning the strong retain into an type-specific retain. */ \
    auto type = storageType.castTo<Name##StorageType>(); \
    /* FIXME: resilience */ \
    if (!type->isLoadable(ResilienceExpansion::Maximal)) { \
      SGF.B.createStore##Name(loc, value, dest, isInit); \
      /* store doesn't take ownership of the input, so cancel it out. */ \
      SGF.B.emitDestroyValueOperation(loc, value); \
      return; \
    } \
    ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name) \
  }
#define UNCHECKED_REF_STORAGE(Name, ...) \
  case ReferenceOwnership::Name: { \
    /* For static reference storage types, we need to enter the box and */ \
    /* release the strong retain. */ \
    auto typedValue = SGF.B.createRefTo##Name(loc, value, \
                                              storageType.getObjectType()); \
    emitUnloweredStoreOfCopy(SGF.B, loc, typedValue, dest, isInit); \
    SGF.B.emitDestroyValueOperation(loc, value); \
    return; \
  }
#include "polarphp/ast/ReferenceStorageDef.h"
#undef ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER
   }
   llvm_unreachable("impossible");
}

/// Load a value of the type-of-rvalue out of the given address as a
/// scalar.  The type-of-rvalue must be loadable.
PILValue PILGenFunction::emitSemanticLoad(PILLocation loc,
                                          PILValue src,
                                          const TypeLowering &srcTL,
                                          const TypeLowering &rvalueTL,
                                          IsTake_t isTake) {
   assert(srcTL.getLoweredType().getAddressType() == src->getType());
   assert(rvalueTL.isLoadable() || !silConv.useLoweredAddresses());

   // Easy case: the types match.
   if (srcTL.getLoweredType() == rvalueTL.getLoweredType()) {
      return srcTL.emitLoadOfCopy(B, loc, src, isTake);
   }

   return emitLoadOfSemanticRValue(*this, loc, src, rvalueTL, isTake);
}

/// Load a value of the type-of-reference out of the given address
/// and into the destination address.
void PILGenFunction::emitSemanticLoadInto(PILLocation loc,
                                          PILValue src,
                                          const TypeLowering &srcTL,
                                          PILValue dest,
                                          const TypeLowering &destTL,
                                          IsTake_t isTake,
                                          IsInitialization_t isInit) {
   assert(srcTL.getLoweredType().getAddressType() == src->getType());
   assert(destTL.getLoweredType().getAddressType() == dest->getType());

   // Easy case: the types match.
   if (srcTL.getLoweredType() == destTL.getLoweredType()) {
      B.createCopyAddr(loc, src, dest, isTake, isInit);
      return;
   }

   auto rvalue = emitLoadOfSemanticRValue(*this, loc, src, srcTL, isTake);
   emitUnloweredStoreOfCopy(B, loc, rvalue, dest, isInit);
}

/// Store an r-value into the given address as an initialization.
void PILGenFunction::emitSemanticStore(PILLocation loc,
                                       PILValue rvalue,
                                       PILValue dest,
                                       const TypeLowering &destTL,
                                       IsInitialization_t isInit) {
   assert(destTL.getLoweredType().getAddressType() == dest->getType());

   // Easy case: the types match.
   if (rvalue->getType() == destTL.getLoweredType()) {
      assert(!silConv.useLoweredAddresses()
             || (destTL.isAddressOnly() == rvalue->getType().isAddress()));
      if (rvalue->getType().isAddress()) {
         B.createCopyAddr(loc, rvalue, dest, IsTake, isInit);
      } else {
         emitUnloweredStoreOfCopy(B, loc, rvalue, dest, isInit);
      }
      return;
   }

   auto &rvalueTL = getTypeLowering(rvalue->getType());
   emitStoreOfSemanticRValue(*this, loc, rvalue, dest, rvalueTL, isInit);
}

/// Convert a semantic rvalue to a value of storage type.
PILValue PILGenFunction::emitConversionFromSemanticValue(PILLocation loc,
                                                         PILValue semanticValue,
                                                         PILType storageType) {
   auto &destTL = getTypeLowering(storageType);
   (void)destTL;
   // Easy case: the types match.
   if (semanticValue->getType() == storageType) {
      return semanticValue;
   }

   auto swiftStorageType = storageType.castTo<ReferenceStorageType>();
   switch (swiftStorageType->getOwnership()) {
      case ReferenceOwnership::Strong:
         llvm_unreachable("strong reference storage type should be impossible");
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
  case ReferenceOwnership::Name: \
    llvm_unreachable("address-only types are never loadable");
#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
  case ReferenceOwnership::Name: { \
    PILValue value = B.createRefTo##Name(loc, semanticValue, storageType); \
    value = B.createCopyValue(loc, value); \
    B.emitDestroyValueOperation(loc, semanticValue); \
    return value; \
  }
#define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
  case ReferenceOwnership::Name: { \
    /* For loadable types, place into a box. */ \
    auto type = storageType.castTo<Name##StorageType>(); \
    assert(type->isLoadable(ResilienceExpansion::Maximal)); \
    (void) type; \
    PILValue value = B.createRefTo##Name(loc, semanticValue, storageType); \
    value = B.createCopyValue(loc, value); \
    B.emitDestroyValueOperation(loc, semanticValue); \
    return value; \
  }
#define UNCHECKED_REF_STORAGE(Name, ...) \
  case ReferenceOwnership::Name: { \
    /* For static reference storage types, place into a box. */ \
    PILValue value = B.createRefTo##Name(loc, semanticValue, storageType); \
    B.emitDestroyValueOperation(loc, semanticValue); \
    return value; \
  }
#include "polarphp/ast/ReferenceStorageDef.h"
   }
   llvm_unreachable("impossible");
}

static void emitTsanInoutAccess(PILGenFunction &SGF, PILLocation loc,
                                ManagedValue address) {
   assert(address.getType().isAddress());
   PILValue accessFnArgs[] = {address.getValue()};

   SGF.B.createBuiltin(loc, SGF.getAstContext().getIdentifier("tsanInoutAccess"),
                       SGF.SGM.Types.getEmptyTupleType(), {}, accessFnArgs);
}

/// Produce a physical address that corresponds to the given l-value
/// component.
static ManagedValue drillIntoComponent(PILGenFunction &SGF,
                                       PILLocation loc,
                                       PathComponent &&component,
                                       ManagedValue base,
                                       TSanKind tsanKind) {
   bool isRValue = component.isRValue();
   ManagedValue addr = std::move(component).project(SGF, loc, base);

   if ((SGF.getModule().getOptions().Sanitizers & SanitizerKind::Thread) &&
       tsanKind == TSanKind::InoutAccess && !isRValue) {
      emitTsanInoutAccess(SGF, loc, addr);
   }

   return addr;
}

/// Find the last component of the given lvalue and derive a base
/// location for it.
static PathComponent &&
drillToLastComponent(PILGenFunction &SGF,
                     PILLocation loc,
                     LValue &&lv,
                     ManagedValue &addr,
                     TSanKind tsanKind = TSanKind::None) {
   assert(lv.begin() != lv.end() &&
          "lvalue must have at least one component");

   for (auto i = lv.begin(), e = lv.end() - 1; i != e; ++i) {
      addr = drillIntoComponent(SGF, loc, std::move(**i), addr, tsanKind);
   }

   return std::move(**(lv.end() - 1));
}

static ArgumentSource emitBaseValueForAccessor(PILGenFunction &SGF,
                                               PILLocation loc, LValue &&lvalue,
                                               CanType baseFormalType,
                                               PILDeclRef accessor) {
   ManagedValue base;
   PathComponent &&component =
      drillToLastComponent(SGF, loc, std::move(lvalue), base);
   base = drillIntoComponent(SGF, loc, std::move(component), base,
                             TSanKind::None);

   return SGF.prepareAccessorBaseArg(loc, base, baseFormalType, accessor);
}

RValue PILGenFunction::emitLoadOfLValue(PILLocation loc, LValue &&src,
                                        SGFContext C, bool isBaseGuaranteed) {
   assert(isReadAccess(src.getAccessKind()));

   // Any writebacks should be scoped to after the load.
   FormalEvaluationScope scope(*this);

   // We shouldn't need to re-abstract here, but we might have to bridge.
   // This should only happen if we have a global variable of NSString type.
   auto origFormalType = src.getOrigFormalType();
   auto substFormalType = src.getSubstFormalType();
   auto &rvalueTL = getTypeLowering(src.getTypeOfRValue());

   ManagedValue addr;
   PathComponent &&component =
      drillToLastComponent(*this, loc, std::move(src), addr);

   // If the last component is physical, drill down and load from it.
   if (component.isPhysical()) {
      auto projection = std::move(component).project(*this, loc, addr);
      if (projection.getType().isAddress()) {
         projection = emitLoad(loc, projection.getValue(),
                               origFormalType, substFormalType,
                               rvalueTL, C, IsNotTake,
                               isBaseGuaranteed);
      } else if (isReadAccessResultOwned(src.getAccessKind()) &&
                 !projection.isPlusOne(*this)) {
         projection = projection.copy(*this, loc);
      }

      return RValue(*this, loc, substFormalType, projection);
   }

   // If the last component is logical, emit a get.
   return std::move(component.asLogical()).get(*this, loc, addr, C);
}

static AbstractionPattern
getFormalStorageAbstractionPattern(PILGenFunction &SGF, AbstractStorageDecl *field) {
   if (auto var = dyn_cast<VarDecl>(field)) {
      auto origType = SGF.SGM.Types.getAbstractionPattern(var);
      return origType.getReferenceStorageReferentType();
   }
   auto sub = cast<SubscriptDecl>(field);
   return SGF.SGM.Types.getAbstractionPattern(sub);
}

/// Produce a singular RValue for a load from the specified property.  This is
/// designed to work with RValue ManagedValue bases that are either +0 or +1.
RValue PILGenFunction::emitRValueForStorageLoad(
   PILLocation loc, ManagedValue base, CanType baseFormalType,
   bool isSuper, AbstractStorageDecl *storage,
   PreparedArguments &&subscriptIndices,
   SubstitutionMap substitutions,
   AccessSemantics semantics, Type propTy, SGFContext C,
   bool isBaseGuaranteed) {
   AccessStrategy strategy =
      storage->getAccessStrategy(semantics, AccessKind::Read,
                                 SGM.M.getPolarphpModule(),
                                 F.getResilienceExpansion());

   // If we should call an accessor of some kind, do so.
   if (strategy.getKind() != AccessStrategy::Storage) {
      auto accessKind = SGFAccessKind::OwnedObjectRead;

      LValue lv = [&] {
         if (!base) return LValue();

         auto baseAccess = getBaseAccessKind(SGM, storage, accessKind,
                                             strategy, baseFormalType);
         return LValue::forValue(baseAccess, base, baseFormalType);
      }();

      lv.addMemberComponent(*this, loc, storage, substitutions, LValueOptions(),
                            isSuper, accessKind, strategy,
                            propTy->getCanonicalType(),
                            std::move(subscriptIndices),
         /*index for diagnostics*/ nullptr);

      return emitLoadOfLValue(loc, std::move(lv), C, isBaseGuaranteed);
   }

   assert(isa<VarDecl>(storage) && "only properties should have storage");
   auto field = cast<VarDecl>(storage);
   assert(field->hasStorage() &&
          "Cannot directly access value without storage");

   // For static variables, emit a reference to the global variable backing
   // them.
   // FIXME: This has to be dynamically looked up for classes, and
   // dynamically instantiated for generics.
   if (field->isStatic()) {
      auto baseMeta = base.getType().castTo<MetatypeType>().getInstanceType();
      (void)baseMeta;
      assert(!baseMeta->is<BoundGenericType>() &&
             "generic static stored properties not implemented");
      if (field->getDeclContext()->getSelfClassDecl() &&
          field->hasStorage())
         // FIXME: don't need to check hasStorage, already done above
         assert(field->isFinal() && "non-final class stored properties not implemented");

      return emitRValueForDecl(loc, field, propTy, semantics, C);
   }


   // rvalue MemberRefExprs are produced in two cases: when accessing a 'let'
   // decl member, and when the base is a (non-lvalue) struct.
   assert(baseFormalType->getAnyNominal() &&
          base.getType().getAstType()->getAnyNominal() &&
          "The base of an rvalue MemberRefExpr should be an rvalue value");

   // If the accessed field is stored, emit a StructExtract on the base.

   auto substFormalType = propTy->getCanonicalType();
   auto &lowering = getTypeLowering(substFormalType);

   // Check for an abstraction difference.
   AbstractionPattern origFormalType =
      getFormalStorageAbstractionPattern(*this, field);
   bool hasAbstractionChange = false;
   auto &abstractedTL = getTypeLowering(origFormalType, substFormalType);
   if (!origFormalType.isExactType(substFormalType)) {
      hasAbstractionChange =
         (abstractedTL.getLoweredType() != lowering.getLoweredType());
   }

   // If the base is a reference type, just handle this as loading the lvalue.
   ManagedValue result;
   if (baseFormalType->hasReferenceSemantics()) {
      LValue LV = emitPropertyLValue(loc, base, baseFormalType, field,
                                     LValueOptions(),
                                     SGFAccessKind::OwnedObjectRead,
                                     AccessSemantics::DirectToStorage);
      auto loaded = emitLoadOfLValue(loc, std::move(LV), C, isBaseGuaranteed);
      // If we don't have to reabstract, the load is sufficient.
      if (!hasAbstractionChange)
         return loaded;

      // Otherwise, bring the component up to +1 so we can reabstract it.
      result = std::move(loaded).getAsSingleValue(*this, loc)
         .copyUnmanaged(*this, loc);
   } else if (!base.getType().isAddress()) {
      // For non-address-only structs, we emit a struct_extract sequence.
      result = B.createStructExtract(loc, base, field);

      if (result.getType().is<ReferenceStorageType>()) {
         // For weak and unowned types, convert the reference to the right
         // pointer, producing a +1.
         result = emitConversionToSemanticRValue(loc, result, lowering);

      } else if (hasAbstractionChange ||
                 (!C.isImmediatePlusZeroOk() &&
                  !(C.isGuaranteedPlusZeroOk() && isBaseGuaranteed))) {
         // If we have an abstraction change or if we have to produce a result at
         // +1, then copy the value. If we know that our base will stay alive for
         // the entire usage of this value, we can borrow the value at +0 for a
         // guaranteed consumer. Otherwise, since we do not have enough information
         // to know if the base's lifetime last's as long as our use of the access,
         // we can only emit at +0 for immediate clients.
         result = result.copyUnmanaged(*this, loc);
      }
   } else {
      // Create a tiny unenforced access scope around a load from local memory. No
      // cleanup is necessary since we directly emit the load here. This will
      // probably go away with opaque values.
      UnenforcedAccess access;
      PILValue accessAddress =
         access.beginAccess(*this, loc, base.getValue(), PILAccessKind::Read);

      // For address-only sequences, the base is in memory.  Emit a
      // struct_element_addr to get to the field, and then load the element as an
      // rvalue.
      PILValue ElementPtr = B.createStructElementAddr(loc, accessAddress, field);

      result = emitLoad(loc, ElementPtr, abstractedTL,
                        hasAbstractionChange ? SGFContext() : C, IsNotTake);
      access.endAccess(*this);
   }

   // If we're accessing this member with an abstraction change, perform that
   // now.
   if (hasAbstractionChange)
      result =
         emitOrigToSubstValue(loc, result, origFormalType, substFormalType, C);
   return RValue(*this, loc, substFormalType, result);
}

ManagedValue PILGenFunction::emitAddressOfLValue(PILLocation loc,
                                                 LValue &&src,
                                                 TSanKind tsanKind) {
   // Write is only included in this list because of property behaviors.
   // It's obviously unreasonable, though.
   assert(src.getAccessKind() == SGFAccessKind::IgnoredRead ||
          src.getAccessKind() == SGFAccessKind::BorrowedAddressRead ||
          src.getAccessKind() == SGFAccessKind::OwnedAddressRead ||
          src.getAccessKind() == SGFAccessKind::ReadWrite ||
          src.getAccessKind() == SGFAccessKind::Write);

   ManagedValue addr;
   PathComponent &&component =
      drillToLastComponent(*this, loc, std::move(src), addr, tsanKind);

   addr = drillIntoComponent(*this, loc, std::move(component), addr, tsanKind);
   assert(addr.getType().isAddress() &&
          "resolving lvalue did not give an address");
   return ManagedValue::forLValue(addr.getValue());
}

ManagedValue PILGenFunction::emitBorrowedLValue(PILLocation loc,
                                                LValue &&src,
                                                TSanKind tsanKind) {
   assert(src.getAccessKind() == SGFAccessKind::IgnoredRead ||
          src.getAccessKind() == SGFAccessKind::BorrowedAddressRead ||
          src.getAccessKind() == SGFAccessKind::BorrowedObjectRead);
   bool isIgnored = src.getAccessKind() == SGFAccessKind::IgnoredRead;

   ManagedValue base;
   PathComponent &&component =
      drillToLastComponent(*this, loc, std::move(src), base, tsanKind);

   auto value =
      drillIntoComponent(*this, loc, std::move(component), base, tsanKind);

   // If project() returned an owned value, and the caller cares, borrow it.
   if (value.hasCleanup() && !isIgnored)
      value = value.formalAccessBorrow(*this, loc);
   return value;
}

LValue
PILGenFunction::emitOpenExistentialLValue(PILLocation loc,
                                          LValue &&lv,
                                          CanArchetypeType openedArchetype,
                                          CanType formalRValueType,
                                          SGFAccessKind accessKind) {
   assert(!formalRValueType->hasLValueType());
   LValueTypeData typeData = {
      accessKind, AbstractionPattern::getOpaque(), formalRValueType,
      getLoweredType(formalRValueType).getAstType()
   };

   // Open up the existential.
   auto rep = lv.getTypeOfRValue()
      .getPreferredExistentialRepresentation();
   switch (rep) {
      case ExistentialRepresentation::Opaque:
      case ExistentialRepresentation::Boxed: {
         lv.add<OpenOpaqueExistentialComponent>(openedArchetype, typeData);
         break;
      }
      case ExistentialRepresentation::Metatype:
      case ExistentialRepresentation::Class: {
         lv.add<OpenNonOpaqueExistentialComponent>(openedArchetype, typeData);
         break;
      }
      case ExistentialRepresentation::None:
         llvm_unreachable("cannot open non-existential");
   }

   return std::move(lv);
}

static bool trySetterPeephole(PILGenFunction &SGF, PILLocation loc,
                              ArgumentSource &&src, LValue &&dest) {
   // The last component must be a getter/setter.
   // TODO: allow reabstraction here, too.
   auto &component = **(dest.end() - 1);
   if (component.getKind() != PathComponent::GetterSetterKind)
      return false;

   // We cannot apply the peephole if the l-value includes an
   // open-existential component because we need to make sure that
   // the opened archetype is available everywhere during emission.
   // TODO: should we instead just immediately open the existential
   // during emitLValue and simply leave the opened address in the LValue?
   // Or is there some reasonable way to detect that this is happening
   // and avoid affecting cases where it is not necessary?
   for (auto &componentPtr : dest) {
      if (componentPtr->isOpenExistential())
         return false;
   }

   auto &setterComponent = static_cast<GetterSetterComponent&>(component);
   if (setterComponent.hasPropertyWrapper())
      return false;
   setterComponent.emitAssignWithSetter(SGF, loc, std::move(dest),
                                        std::move(src));
   return true;;
}

void PILGenFunction::emitAssignToLValue(PILLocation loc, RValue &&src,
                                        LValue &&dest) {
   emitAssignToLValue(loc, ArgumentSource(loc, std::move(src)), std::move(dest));
}

void PILGenFunction::emitAssignToLValue(PILLocation loc,
                                        ArgumentSource &&src,
                                        LValue &&dest) {
   // Enter a FormalEvaluationScope so that formal access to independent LValue
   // components do not overlap. Furthermore, use an ArgumentScope to force
   // cleanup of materialized LValues immediately, before evaluating the next
   // LValue. For example: (x[i], x[j]) = a, b
   ArgumentScope argScope(*this, loc);

   // If the last component is a getter/setter component, use a special
   // generation pattern that allows us to peephole the emission of the RHS.
   if (trySetterPeephole(*this, loc, std::move(src), std::move(dest))) {
      argScope.pop();
      return;
   }

   // Otherwise, force the RHS now to preserve evaluation order.
   auto srcLoc = src.getLocation();
   RValue srcValue = std::move(src).getAsRValue(*this);

   // Peephole: instead of materializing and then assigning into a
   // translation component, untransform the value first.
   while (dest.isLastComponentTranslation()) {
      srcValue = std::move(dest.getLastTranslationComponent())
         .untranslate(*this, loc, std::move(srcValue));
      dest.dropLastTranslationComponent();
   }

   src = ArgumentSource(srcLoc, std::move(srcValue));

   // Resolve all components up to the last, keeping track of value-type logical
   // properties we need to write back to.
   ManagedValue destAddr;
   PathComponent &&component =
      drillToLastComponent(*this, loc, std::move(dest), destAddr);

   // Write to the tail component.
   if (component.isPhysical()) {
      auto finalDestAddr =
         std::move(component).project(*this, loc, destAddr);
      assert(finalDestAddr.getType().isAddress());

      auto value = std::move(src).getAsRValue(*this).ensurePlusOne(*this, loc);
      std::move(value).assignInto(*this, loc, finalDestAddr.getValue());
   } else {
      std::move(component.asLogical()).set(*this, loc, std::move(src), destAddr);
   }

   // The writeback scope closing will propagate the value back up through the
   // writeback chain.
   argScope.pop();
}

void PILGenFunction::emitCopyLValueInto(PILLocation loc, LValue &&src,
                                        Initialization *dest) {
   auto skipPeephole = [&]{
      auto loaded = emitLoadOfLValue(loc, std::move(src), SGFContext(dest));
      if (!loaded.isInContext())
         std::move(loaded).forwardInto(*this, loc, dest);
   };

   // If the source is a physical lvalue, the destination is a single address,
   // and there's no semantic conversion necessary, do a copy_addr from the
   // lvalue into the destination.
   if (!src.isPhysical())
      return skipPeephole();
   if (!dest->canPerformInPlaceInitialization())
      return skipPeephole();
   auto destAddr = dest->getAddressForInPlaceInitialization(*this, loc);
   assert(src.getTypeOfRValue().getAstType()
          == destAddr->getType().getAstType());

   auto srcAddr = emitAddressOfLValue(loc, std::move(src)).getUnmanagedValue();

   UnenforcedAccess access;
   PILValue accessAddress =
      access.beginAccess(*this, loc, destAddr, PILAccessKind::Modify);
   B.createCopyAddr(loc, srcAddr, accessAddress, IsNotTake, IsInitialization);
   access.endAccess(*this);

   dest->finishInitialization(*this);
}

void PILGenFunction::emitAssignLValueToLValue(PILLocation loc, LValue &&src,
                                              LValue &&dest) {
   // Only perform the peephole if both operands are physical, there's no
   // semantic conversion necessary, and exclusivity enforcement
   // is not enabled. The peephole interferes with exclusivity enforcement
   // because it causes the formal accesses to the source and destination to
   // overlap.
   bool peepholeConflict =
      !src.isObviouslyNonConflicting(dest, SGFAccessKind::OwnedObjectRead,
                                     SGFAccessKind::Write);

   if (peepholeConflict || !src.isPhysical() || !dest.isPhysical()) {
      RValue loaded = emitLoadOfLValue(loc, std::move(src), SGFContext());
      emitAssignToLValue(loc, std::move(loaded), std::move(dest));
      return;
   }

   auto &rvalueTL = getTypeLowering(src.getTypeOfRValue());

   auto srcAddr = emitAddressOfLValue(loc, std::move(src)).getUnmanagedValue();
   auto destAddr = emitAddressOfLValue(loc, std::move(dest)).getUnmanagedValue();

   if (srcAddr->getType() == destAddr->getType()) {
      B.createCopyAddr(loc, srcAddr, destAddr, IsNotTake, IsNotInitialization);
   } else {
      // If there's a semantic conversion necessary, do a load then assign.
      auto loaded = emitLoad(loc, srcAddr, rvalueTL, SGFContext(), IsNotTake);
      loaded.assignInto(*this, loc, destAddr);
   }
}
